@shival99/z-ui 1.2.1 → 1.2.6
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/fesm2022/shival99-z-ui-components-z-calendar.mjs +14 -14
- package/fesm2022/shival99-z-ui-components-z-calendar.mjs.map +1 -1
- package/fesm2022/shival99-z-ui-components-z-checkbox.mjs +4 -7
- package/fesm2022/shival99-z-ui-components-z-checkbox.mjs.map +1 -1
- package/fesm2022/shival99-z-ui-components-z-code.mjs +3 -4
- package/fesm2022/shival99-z-ui-components-z-code.mjs.map +1 -1
- package/fesm2022/shival99-z-ui-components-z-editor.mjs +11 -12
- package/fesm2022/shival99-z-ui-components-z-editor.mjs.map +1 -1
- package/fesm2022/shival99-z-ui-components-z-input.mjs +6 -8
- package/fesm2022/shival99-z-ui-components-z-input.mjs.map +1 -1
- package/fesm2022/shival99-z-ui-components-z-modal.mjs +7 -8
- package/fesm2022/shival99-z-ui-components-z-modal.mjs.map +1 -1
- package/fesm2022/shival99-z-ui-components-z-pagination.mjs +11 -13
- package/fesm2022/shival99-z-ui-components-z-pagination.mjs.map +1 -1
- package/fesm2022/shival99-z-ui-components-z-table.mjs +28 -50
- package/fesm2022/shival99-z-ui-components-z-table.mjs.map +1 -1
- package/fesm2022/shival99-z-ui-components-z-upload.mjs +10 -10
- package/fesm2022/shival99-z-ui-components-z-upload.mjs.map +1 -1
- package/fesm2022/shival99-z-ui-pipes.mjs +5 -1
- package/fesm2022/shival99-z-ui-pipes.mjs.map +1 -1
- package/fesm2022/shival99-z-ui-utils.mjs.map +1 -1
- package/package.json +1 -1
- package/types/shival99-z-ui-components-z-calendar.d.ts +3 -4
- package/types/shival99-z-ui-components-z-checkbox.d.ts +2 -3
- package/types/shival99-z-ui-components-z-code.d.ts +1 -1
- package/types/shival99-z-ui-components-z-editor.d.ts +3 -4
- package/types/shival99-z-ui-components-z-input.d.ts +2 -3
- package/types/shival99-z-ui-components-z-modal.d.ts +2 -3
- package/types/shival99-z-ui-components-z-pagination.d.ts +4 -5
- package/types/shival99-z-ui-components-z-table.d.ts +4 -10
- package/types/shival99-z-ui-components-z-upload.d.ts +1 -2
- package/types/shival99-z-ui-pipes.d.ts +1 -0
- package/types/shival99-z-ui-utils.d.ts +1 -1
|
@@ -3,7 +3,7 @@ import { ScrollingModule } from '@angular/cdk/scrolling';
|
|
|
3
3
|
import { NgClass, NgStyle, NgTemplateOutlet } from '@angular/common';
|
|
4
4
|
import * as i0 from '@angular/core';
|
|
5
5
|
import { input, output, computed, ChangeDetectionStrategy, Component, inject, ElementRef, Renderer2, Directive, NgZone, Pipe, TemplateRef, DestroyRef, signal, viewChild, untracked, effect, afterNextRender } from '@angular/core';
|
|
6
|
-
import {
|
|
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';
|
|
9
9
|
import { ZCheckboxComponent } from '@shival99/z-ui/components/z-checkbox';
|
|
@@ -22,12 +22,10 @@ import { createAngularTable, getFacetedMinMaxValues, getFacetedUniqueValues, get
|
|
|
22
22
|
import { NgScrollbar } from 'ngx-scrollbar';
|
|
23
23
|
import { explicitEffect } from 'ngxtension/explicit-effect';
|
|
24
24
|
import { ZDropdownMenuComponent } from '@shival99/z-ui/components/z-dropdown-menu';
|
|
25
|
-
import { toSignal } from '@angular/core/rxjs-interop';
|
|
26
25
|
import * as i1 from '@angular/forms';
|
|
27
26
|
import { FormsModule } from '@angular/forms';
|
|
28
27
|
import { ZCalendarComponent } from '@shival99/z-ui/components/z-calendar';
|
|
29
28
|
import { ZSelectComponent } from '@shival99/z-ui/components/z-select';
|
|
30
|
-
import { startWith } from 'rxjs';
|
|
31
29
|
|
|
32
30
|
const DEFAULT_MAX_VISIBLE = 3;
|
|
33
31
|
const DEFAULT_DROPDOWN_BUTTON_SIZE = 'sm';
|
|
@@ -210,8 +208,6 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImpor
|
|
|
210
208
|
class ZTableFilterComponent {
|
|
211
209
|
zColumn = input.required(...(ngDevMode ? [{ debugName: "zColumn" }] : []));
|
|
212
210
|
zTable = input.required(...(ngDevMode ? [{ debugName: "zTable" }] : []));
|
|
213
|
-
_translate = inject(TranslateService);
|
|
214
|
-
_currentLang = toSignal(this._translate.onLangChange.pipe(startWith({ lang: this._translate.currentLang })));
|
|
215
211
|
_debounceTimeout = null;
|
|
216
212
|
_debounceMs = 300;
|
|
217
213
|
_columnDef = computed(() => this.zColumn().columnDef, ...(ngDevMode ? [{ debugName: "_columnDef" }] : []));
|
|
@@ -233,30 +229,6 @@ class ZTableFilterComponent {
|
|
|
233
229
|
}
|
|
234
230
|
return value[1] ?? '';
|
|
235
231
|
}, ...(ngDevMode ? [{ debugName: "rangeMaxValue" }] : []));
|
|
236
|
-
minPlaceholder = computed(() => {
|
|
237
|
-
this._currentLang();
|
|
238
|
-
return this._translate.instant('i18n_z_ui_table_filter_min');
|
|
239
|
-
}, ...(ngDevMode ? [{ debugName: "minPlaceholder" }] : []));
|
|
240
|
-
maxPlaceholder = computed(() => {
|
|
241
|
-
this._currentLang();
|
|
242
|
-
return this._translate.instant('i18n_z_ui_table_filter_max');
|
|
243
|
-
}, ...(ngDevMode ? [{ debugName: "maxPlaceholder" }] : []));
|
|
244
|
-
searchPlaceholder = computed(() => {
|
|
245
|
-
this._currentLang();
|
|
246
|
-
return this._translate.instant('i18n_z_ui_table_filter_search');
|
|
247
|
-
}, ...(ngDevMode ? [{ debugName: "searchPlaceholder" }] : []));
|
|
248
|
-
allLabel = computed(() => {
|
|
249
|
-
this._currentLang();
|
|
250
|
-
return this._translate.instant('i18n_z_ui_table_filter_all');
|
|
251
|
-
}, ...(ngDevMode ? [{ debugName: "allLabel" }] : []));
|
|
252
|
-
datePlaceholder = computed(() => {
|
|
253
|
-
this._currentLang();
|
|
254
|
-
return this._translate.instant('i18n_z_ui_table_filter_date');
|
|
255
|
-
}, ...(ngDevMode ? [{ debugName: "datePlaceholder" }] : []));
|
|
256
|
-
dateRangePlaceholder = computed(() => {
|
|
257
|
-
this._currentLang();
|
|
258
|
-
return this._translate.instant('i18n_z_ui_table_filter_date_range');
|
|
259
|
-
}, ...(ngDevMode ? [{ debugName: "dateRangePlaceholder" }] : []));
|
|
260
232
|
dateValue = computed(() => {
|
|
261
233
|
const value = this.columnFilterValue();
|
|
262
234
|
if (value instanceof Date) {
|
|
@@ -273,12 +245,12 @@ class ZTableFilterComponent {
|
|
|
273
245
|
}, ...(ngDevMode ? [{ debugName: "dateRangeValue" }] : []));
|
|
274
246
|
selectOptions = computed(() => {
|
|
275
247
|
const columnDef = this._columnDef();
|
|
276
|
-
|
|
248
|
+
// Note: 'All' option is handled via zPlaceholder in template
|
|
277
249
|
if (columnDef.filterOptions && columnDef.filterOptions.length > 0) {
|
|
278
|
-
return
|
|
250
|
+
return columnDef.filterOptions;
|
|
279
251
|
}
|
|
280
252
|
const uniqueValues = Array.from(this.zColumn().getFacetedUniqueValues().keys()).sort().slice(0, 5000);
|
|
281
|
-
return
|
|
253
|
+
return uniqueValues.map(v => ({ label: String(v), value: String(v) }));
|
|
282
254
|
}, ...(ngDevMode ? [{ debugName: "selectOptions" }] : []));
|
|
283
255
|
ngOnDestroy() {
|
|
284
256
|
if (this._debounceTimeout) {
|
|
@@ -333,7 +305,7 @@ class ZTableFilterComponent {
|
|
|
333
305
|
<z-input
|
|
334
306
|
zType="number"
|
|
335
307
|
zSize="sm"
|
|
336
|
-
[zPlaceholder]="
|
|
308
|
+
[zPlaceholder]="'i18n_z_ui_table_filter_min' | translate"
|
|
337
309
|
[ngModel]="rangeMinValue()"
|
|
338
310
|
(ngModelChange)="onMinChangeDebounced($event)"
|
|
339
311
|
class="min-w-0 flex-1"
|
|
@@ -341,7 +313,7 @@ class ZTableFilterComponent {
|
|
|
341
313
|
<z-input
|
|
342
314
|
zType="number"
|
|
343
315
|
zSize="sm"
|
|
344
|
-
[zPlaceholder]="
|
|
316
|
+
[zPlaceholder]="'i18n_z_ui_table_filter_max' | translate"
|
|
345
317
|
[ngModel]="rangeMaxValue()"
|
|
346
318
|
(ngModelChange)="onMaxChangeDebounced($event)"
|
|
347
319
|
class="min-w-0 flex-1"
|
|
@@ -354,7 +326,7 @@ class ZTableFilterComponent {
|
|
|
354
326
|
zSize="sm"
|
|
355
327
|
zAllowClear
|
|
356
328
|
zValueType="date"
|
|
357
|
-
[zPlaceholder]="
|
|
329
|
+
[zPlaceholder]="'i18n_z_ui_table_filter_date' | translate"
|
|
358
330
|
[ngModel]="dateValue()"
|
|
359
331
|
(ngModelChange)="onDateChange($event)"
|
|
360
332
|
class="w-full"
|
|
@@ -366,7 +338,7 @@ class ZTableFilterComponent {
|
|
|
366
338
|
zSize="sm"
|
|
367
339
|
zAllowClear
|
|
368
340
|
zValueType="date"
|
|
369
|
-
[zPlaceholder]="
|
|
341
|
+
[zPlaceholder]="'i18n_z_ui_table_filter_date_range' | translate"
|
|
370
342
|
[ngModel]="dateRangeValue()"
|
|
371
343
|
(ngModelChange)="onDateRangeChange($event)"
|
|
372
344
|
class="w-full"
|
|
@@ -377,7 +349,7 @@ class ZTableFilterComponent {
|
|
|
377
349
|
zSize="sm"
|
|
378
350
|
[zOptions]="selectOptions()"
|
|
379
351
|
[ngModel]="columnFilterValue()?.toString() ?? ''"
|
|
380
|
-
[zPlaceholder]="
|
|
352
|
+
[zPlaceholder]="'i18n_z_ui_table_filter_all' | translate"
|
|
381
353
|
(ngModelChange)="onSelectChange($event)"
|
|
382
354
|
class="w-full min-w-0"
|
|
383
355
|
/>
|
|
@@ -387,7 +359,7 @@ class ZTableFilterComponent {
|
|
|
387
359
|
zType="number"
|
|
388
360
|
zSize="sm"
|
|
389
361
|
zAllowClear
|
|
390
|
-
[zPlaceholder]="
|
|
362
|
+
[zPlaceholder]="'i18n_z_ui_table_filter_search' | translate"
|
|
391
363
|
[ngModel]="columnFilterValue()?.toString() ?? ''"
|
|
392
364
|
(ngModelChange)="onTextChangeDebounced($event)"
|
|
393
365
|
class="w-full"
|
|
@@ -398,20 +370,20 @@ class ZTableFilterComponent {
|
|
|
398
370
|
zType="text"
|
|
399
371
|
zSize="sm"
|
|
400
372
|
zAllowClear
|
|
401
|
-
[zPlaceholder]="
|
|
373
|
+
[zPlaceholder]="'i18n_z_ui_table_filter_search' | translate"
|
|
402
374
|
[ngModel]="(columnFilterValue() ?? '').toString()"
|
|
403
375
|
(ngModelChange)="onTextChangeDebounced($event)"
|
|
404
376
|
class="w-full"
|
|
405
377
|
/>
|
|
406
378
|
}
|
|
407
379
|
}
|
|
408
|
-
`, isInline: true, 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"], 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", "zSelectedTemplate", "zOptionTemplate", "zActionTemplate", "zAsyncValidators", "zAsyncDebounce", "zAsyncValidateOn", "zValidators"], outputs: ["zOnSearch", "zOnLoadMore", "zControl"], 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", "zLocale", "zShowOk", "zOkText", "zShowCancel", "zCancelText", "zDisabledDate"], outputs: ["zControl", "zChange"], exportAs: ["zCalendar"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
380
|
+
`, isInline: true, 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"], 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", "zSelectedTemplate", "zOptionTemplate", "zActionTemplate", "zAsyncValidators", "zAsyncDebounce", "zAsyncValidateOn", "zValidators"], outputs: ["zOnSearch", "zOnLoadMore", "zControl"], 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", "zLocale", "zShowOk", "zOkText", "zShowCancel", "zCancelText", "zDisabledDate"], outputs: ["zControl", "zChange"], exportAs: ["zCalendar"] }, { kind: "pipe", type: TranslatePipe, name: "translate" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
409
381
|
}
|
|
410
382
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: ZTableFilterComponent, decorators: [{
|
|
411
383
|
type: Component,
|
|
412
384
|
args: [{
|
|
413
385
|
selector: 'z-table-filter',
|
|
414
|
-
imports: [FormsModule, ZInputComponent, ZSelectComponent, ZCalendarComponent],
|
|
386
|
+
imports: [FormsModule, ZInputComponent, ZSelectComponent, ZCalendarComponent, TranslatePipe],
|
|
415
387
|
standalone: true,
|
|
416
388
|
template: `
|
|
417
389
|
@switch (filterType()) {
|
|
@@ -420,7 +392,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImpor
|
|
|
420
392
|
<z-input
|
|
421
393
|
zType="number"
|
|
422
394
|
zSize="sm"
|
|
423
|
-
[zPlaceholder]="
|
|
395
|
+
[zPlaceholder]="'i18n_z_ui_table_filter_min' | translate"
|
|
424
396
|
[ngModel]="rangeMinValue()"
|
|
425
397
|
(ngModelChange)="onMinChangeDebounced($event)"
|
|
426
398
|
class="min-w-0 flex-1"
|
|
@@ -428,7 +400,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImpor
|
|
|
428
400
|
<z-input
|
|
429
401
|
zType="number"
|
|
430
402
|
zSize="sm"
|
|
431
|
-
[zPlaceholder]="
|
|
403
|
+
[zPlaceholder]="'i18n_z_ui_table_filter_max' | translate"
|
|
432
404
|
[ngModel]="rangeMaxValue()"
|
|
433
405
|
(ngModelChange)="onMaxChangeDebounced($event)"
|
|
434
406
|
class="min-w-0 flex-1"
|
|
@@ -441,7 +413,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImpor
|
|
|
441
413
|
zSize="sm"
|
|
442
414
|
zAllowClear
|
|
443
415
|
zValueType="date"
|
|
444
|
-
[zPlaceholder]="
|
|
416
|
+
[zPlaceholder]="'i18n_z_ui_table_filter_date' | translate"
|
|
445
417
|
[ngModel]="dateValue()"
|
|
446
418
|
(ngModelChange)="onDateChange($event)"
|
|
447
419
|
class="w-full"
|
|
@@ -453,7 +425,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImpor
|
|
|
453
425
|
zSize="sm"
|
|
454
426
|
zAllowClear
|
|
455
427
|
zValueType="date"
|
|
456
|
-
[zPlaceholder]="
|
|
428
|
+
[zPlaceholder]="'i18n_z_ui_table_filter_date_range' | translate"
|
|
457
429
|
[ngModel]="dateRangeValue()"
|
|
458
430
|
(ngModelChange)="onDateRangeChange($event)"
|
|
459
431
|
class="w-full"
|
|
@@ -464,7 +436,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImpor
|
|
|
464
436
|
zSize="sm"
|
|
465
437
|
[zOptions]="selectOptions()"
|
|
466
438
|
[ngModel]="columnFilterValue()?.toString() ?? ''"
|
|
467
|
-
[zPlaceholder]="
|
|
439
|
+
[zPlaceholder]="'i18n_z_ui_table_filter_all' | translate"
|
|
468
440
|
(ngModelChange)="onSelectChange($event)"
|
|
469
441
|
class="w-full min-w-0"
|
|
470
442
|
/>
|
|
@@ -474,7 +446,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImpor
|
|
|
474
446
|
zType="number"
|
|
475
447
|
zSize="sm"
|
|
476
448
|
zAllowClear
|
|
477
|
-
[zPlaceholder]="
|
|
449
|
+
[zPlaceholder]="'i18n_z_ui_table_filter_search' | translate"
|
|
478
450
|
[ngModel]="columnFilterValue()?.toString() ?? ''"
|
|
479
451
|
(ngModelChange)="onTextChangeDebounced($event)"
|
|
480
452
|
class="w-full"
|
|
@@ -485,7 +457,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImpor
|
|
|
485
457
|
zType="text"
|
|
486
458
|
zSize="sm"
|
|
487
459
|
zAllowClear
|
|
488
|
-
[zPlaceholder]="
|
|
460
|
+
[zPlaceholder]="'i18n_z_ui_table_filter_search' | translate"
|
|
489
461
|
[ngModel]="(columnFilterValue() ?? '').toString()"
|
|
490
462
|
(ngModelChange)="onTextChangeDebounced($event)"
|
|
491
463
|
class="w-full"
|
|
@@ -1956,6 +1928,7 @@ class ZTableComponent {
|
|
|
1956
1928
|
hasScrollRight = signal(false, ...(ngDevMode ? [{ debugName: "hasScrollRight" }] : []));
|
|
1957
1929
|
_columnPinVersion = signal(0, ...(ngDevMode ? [{ debugName: "_columnPinVersion" }] : []));
|
|
1958
1930
|
_dataForceUpdate = signal(0, ...(ngDevMode ? [{ debugName: "_dataForceUpdate" }] : []));
|
|
1931
|
+
_configLoadedFromCache = signal(false, ...(ngDevMode ? [{ debugName: "_configLoadedFromCache" }] : []));
|
|
1959
1932
|
pinnedColumnIds = computed(() => {
|
|
1960
1933
|
this._columnPinVersion();
|
|
1961
1934
|
return this.columnOrder().filter(id => {
|
|
@@ -2484,6 +2457,10 @@ class ZTableComponent {
|
|
|
2484
2457
|
const cfg = this.zConfig();
|
|
2485
2458
|
const initial = cfg.initialState;
|
|
2486
2459
|
untracked(() => {
|
|
2460
|
+
// Skip if config was already loaded from cache
|
|
2461
|
+
if (this._configLoadedFromCache()) {
|
|
2462
|
+
return;
|
|
2463
|
+
}
|
|
2487
2464
|
if (initial) {
|
|
2488
2465
|
if (initial.columnPinning) {
|
|
2489
2466
|
this.columnPinning.set(initial.columnPinning);
|
|
@@ -3059,6 +3036,7 @@ class ZTableComponent {
|
|
|
3059
3036
|
}
|
|
3060
3037
|
if (config.columnPinning) {
|
|
3061
3038
|
this.columnPinning.set(config.columnPinning);
|
|
3039
|
+
this._configLoadedFromCache.set(true);
|
|
3062
3040
|
}
|
|
3063
3041
|
if (config.columnVisibility) {
|
|
3064
3042
|
this.columnVisibility.set(config.columnVisibility);
|
|
@@ -3114,7 +3092,7 @@ class ZTableComponent {
|
|
|
3114
3092
|
});
|
|
3115
3093
|
}
|
|
3116
3094
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: ZTableComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
3117
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.6", type: ZTableComponent, isStandalone: true, selector: "z-table", inputs: { 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-2" }, 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 }], exportAs: ["zTable"], ngImport: i0, template: "<!-- Toolbar: Search & Settings -->\n@if (zConfig().enableSearch || zConfig().enableSettings) {\n <div class=\"z-table-toolbar mb-2 flex items-center justify-between gap-4\">\n <!-- Search -->\n @if (zConfig().enableSearch) {\n <z-input\n class=\"w-64\"\n zSize=\"sm\"\n [zPlaceholder]=\"'i18n_z_ui_table_search' | translate\"\n [zSearch]=\"true\"\n [zDebounce]=\"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=\"z-table-container shadow-xs\"\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 <col [style.width]=\"'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 | zTableHeaderRender: headerGroup.headers : zConfig().columns;\n @if (rowSpan && shouldRender) {\n <th\n [attr.id]=\"header.column.id\"\n [ngStyle]=\"header.column | zTablePinningStyles: header : 'header' : table : zConfig().columns\"\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 | zTableHeaderPin: 'isLastLeftPinned' : zConfig().columns\"\n [class.z-sticky-right-first]=\"header | zTableHeaderPin: 'isFirstRightPinned' : zConfig().columns\"\n [class.z-sticky-right-last]=\"header | zTableHeaderPin: '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 zSize=\"sm\"\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 [zTableResizer]=\"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 (zLoading() || isProcessing()) {\n <!-- Loading State -->\n @if (zConfig().useSkeleton) {\n <!-- Skeleton Loading -->\n <div class=\"z-skeleton-state\">\n @for (i of skeletonRows(); track $index) {\n <div class=\"z-skeleton-row\" [style.height.px]=\"virtualRowHeight()\">\n <z-skeleton zType=\"bar\" zWidth=\"100%\" [zHeight]=\"virtualRowHeight() - 16 + 'px'\" />\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 class=\"z-virtual-row\"\n [style.height.px]=\"dynamicGroupHeights()[virtualItem.index] ?? groupSize() * virtualRowHeight()\"\n [style.transform]=\"'translateY(' + virtualItem.start + 'px)'\">\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]=\"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 @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 | zTableBodyCellRender: row.getVisibleCells() : zConfig().columns;\n @if (shouldRenderRowSpan && shouldRenderColSpan) {\n <td\n [ngStyle]=\"\n cell.column\n | zTablePinningStyles: cell : 'body' : row.getVisibleCells() : zConfig().columns\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 zSize=\"sm\"\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 <ng-container\n *flexRender=\"cell.column.columnDef.cell; props: cell.getContext(); let cellContent\">\n @if (cellContent | zTableIsTemplateRef) {\n <!-- TemplateRef rendering -->\n <div\n [ngClass]=\"cell | zTableCellConfig: zConfig().columns : 'contentClass'\"\n [ngStyle]=\"cell | zTableCellConfig: zConfig().columns : 'contentStyle'\">\n <ng-container\n *ngTemplateOutlet=\"cellContent; context: { $implicit: cell.getContext() }\" />\n </div>\n } @else if (cellContent | zTableHasIcon) {\n <!-- Icon syntax rendering -->\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 } @else {\n <!-- Default/innerHTML rendering -->\n <div\n class=\"z-table-cell-text\"\n z-tooltip\n [zContent]=\"\n (cell | zTableCellConfig: zConfig().columns : 'contentTooltip') || cellContent\n \"\n [innerHTML]=\"cellContent | zSafeHtml\"\n [ngClass]=\"cell | zTableCellConfig: zConfig().columns : 'contentClass'\"\n [ngStyle]=\"cell | zTableCellConfig: zConfig().columns : 'contentStyle'\"></div>\n }\n </ng-container>\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 = cell | zTableBodyCellRender: row.getVisibleCells() : zConfig().columns;\n @if (shouldRenderRowSpan && shouldRenderColSpan) {\n <td\n [ngStyle]=\"\n cell.column | zTablePinningStyles: cell : 'body' : row.getVisibleCells() : zConfig().columns\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 zSize=\"sm\"\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 <ng-container\n *flexRender=\"cell.column.columnDef.cell; props: cell.getContext(); let cellContent\">\n @if (cellContent | zTableIsTemplateRef) {\n <!-- TemplateRef rendering -->\n <div\n [ngClass]=\"cell | zTableCellConfig: zConfig().columns : 'contentClass'\"\n [ngStyle]=\"cell | zTableCellConfig: zConfig().columns : 'contentStyle'\">\n <ng-container\n *ngTemplateOutlet=\"cellContent; context: { $implicit: cell.getContext() }\" />\n </div>\n } @else if (cellContent | zTableHasIcon) {\n <!-- Icon syntax rendering -->\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 } @else {\n <!-- Default/innerHTML rendering -->\n <div\n class=\"z-table-cell-text\"\n z-tooltip\n [zContent]=\"\n (cell | zTableCellConfig: zConfig().columns : 'contentTooltip') || cellContent\n \"\n [innerHTML]=\"cellContent | zSafeHtml\"\n [ngClass]=\"cell | zTableCellConfig: zConfig().columns : 'contentClass'\"\n [ngStyle]=\"cell | zTableCellConfig: zConfig().columns : 'contentStyle'\"></div>\n }\n </ng-container>\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 | zTableFooterRender: footerGroup.headers : zConfig().columns;\n @if (rowSpan && shouldRender) {\n <th\n [ngStyle]=\"footer.column | zTablePinningStyles: footer : 'header' : table : zConfig().columns\"\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]=\"footer | zTableHeaderPin: 'isLastLeftPinned' : zConfig().columns\"\n [class.z-sticky-right-first]=\"footer | zTableHeaderPin: 'isFirstRightPinned' : zConfig().columns\"\n [class.z-sticky-right-last]=\"footer | zTableHeaderPin: 'isLastRightPinned' : zConfig().columns\"\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().enablePagination && 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=\"text-sm text-gray-500\">\n {{\n ('i18n_z_ui_table_total_rows' | translate).replace(\n '{total}',\n zConfig().totalCount?.toString() || totalRows.toString()\n )\n }}\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 || zLoading() || 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 zSize=\"sm\"\n [zChecked]=\"showHorizontalBorder()\"\n [zText]=\"'i18n_z_ui_table_horizontal_border' | translate\"\n (zChange)=\"showHorizontalBorder.set(!showHorizontalBorder())\" />\n <z-checkbox\n zSize=\"sm\"\n [zChecked]=\"showVerticalBorder()\"\n [zText]=\"'i18n_z_ui_table_vertical_border' | translate\"\n (zChange)=\"showVerticalBorder.set(!showVerticalBorder())\" />\n <z-checkbox\n zSize=\"sm\"\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 }}\n </span>\n @let parents = columnId | zTableColumnParents: zConfig().columns;\n @if (parents) {\n <span class=\"text-muted-foreground truncate text-[10px]\">({{ parents }})</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 <!-- 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 }}\n </span>\n @let pinnedParents = columnId | zTableColumnParents: zConfig().columns;\n @if (pinnedParents) {\n <span class=\"text-muted-foreground truncate text-[10px]\">({{ pinnedParents }})</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: [":host{display:flex;flex-direction:column;height:100%;--scrollbar-track-thickness: 7px;--scrollbar-track-color: transparent;--scrollbar-thumb-shape: 3px}.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:auto;overflow-y:hidden;scrollbar-width:none}.z-thead-wrapper::-webkit-scrollbar{display:none}.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-skeleton-state{display:flex;flex-direction:column;min-height:200px;height:100%;padding:0;animation:z-fade-in .2s ease-out}.z-skeleton-state .z-skeleton-row{display:flex;align-items:center;padding:0 8px;border-bottom:thin solid var(--border);box-sizing:border-box}.z-skeleton-state .z-skeleton-row:last-child{border-bottom:none}.z-skeleton-state .z-skeleton-row z-skeleton{display:flex;align-items:center;width:100%;height:auto!important}.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%,transparent)!important}tbody tr.z-selected:hover,tbody tr.z-selected:hover td{background-color:color-mix(in srgb,var(--primary) 20%,transparent)!important}tbody tr.z-indeterminate-selected,tbody tr.z-indeterminate-selected td{background-color:color-mix(in srgb,var(--primary) 10%,transparent)!important}tbody tr.z-indeterminate-selected:hover,tbody tr.z-indeterminate-selected:hover td{background-color:color-mix(in srgb,var(--primary) 15%,transparent)!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}.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:-30px;width:30px;pointer-events:none;box-shadow:inset 10px 0 8px -8px #0000001a;z-index:10;opacity:0;transition:opacity .2s ease}.z-thead-wrapper.z-scroll-left th.z-sticky-left-last:after,.z-thead-wrapper.z-scroll-left td.z-sticky-left-last:after,.z-tbody-wrapper.z-scroll-left th.z-sticky-left-last:after,.z-tbody-wrapper.z-scroll-left td.z-sticky-left-last:after,.z-tfoot-wrapper.z-scroll-left th.z-sticky-left-last:after,.z-tfoot-wrapper.z-scroll-left td.z-sticky-left-last:after{opacity:1}.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}.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:-30px;width:30px;pointer-events:none;box-shadow:inset -10px 0 8px -8px #0000001a;z-index:10;opacity:0;transition:opacity .2s ease}.z-thead-wrapper.z-scroll-right th.z-sticky-right-first,.z-thead-wrapper.z-scroll-right td.z-sticky-right-first,.z-tbody-wrapper.z-scroll-right th.z-sticky-right-first,.z-tbody-wrapper.z-scroll-right td.z-sticky-right-first,.z-tfoot-wrapper.z-scroll-right th.z-sticky-right-first,.z-tfoot-wrapper.z-scroll-right td.z-sticky-right-first{border-left:none}.z-thead-wrapper.z-scroll-right th.z-sticky-right-first:before,.z-thead-wrapper.z-scroll-right td.z-sticky-right-first:before,.z-tbody-wrapper.z-scroll-right th.z-sticky-right-first:before,.z-tbody-wrapper.z-scroll-right td.z-sticky-right-first:before,.z-tfoot-wrapper.z-scroll-right th.z-sticky-right-first:before,.z-tfoot-wrapper.z-scroll-right td.z-sticky-right-first:before{opacity:1}.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:auto;overflow-y:hidden;scrollbar-width:none}.z-tfoot-wrapper::-webkit-scrollbar{display:none}.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}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%,transparent);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}:host-context(.dark) .z-thead-wrapper.z-scroll-left th.z-sticky-left-last,:host-context(.dark) .z-thead-wrapper.z-scroll-left td.z-sticky-left-last,:host-context(.dark) .z-tbody-wrapper.z-scroll-left th.z-sticky-left-last,:host-context(.dark) .z-tbody-wrapper.z-scroll-left td.z-sticky-left-last,:host-context(.dark) .z-tfoot-wrapper.z-scroll-left th.z-sticky-left-last,:host-context(.dark) .z-tfoot-wrapper.z-scroll-left td.z-sticky-left-last{border-right:1px solid var(--border)}:host-context(.dark) .z-thead-wrapper.z-scroll-right th.z-sticky-right-first,:host-context(.dark) .z-thead-wrapper.z-scroll-right td.z-sticky-right-first,:host-context(.dark) .z-tbody-wrapper.z-scroll-right th.z-sticky-right-first,:host-context(.dark) .z-tbody-wrapper.z-scroll-right td.z-sticky-right-first,:host-context(.dark) .z-tfoot-wrapper.z-scroll-right th.z-sticky-right-first,:host-context(.dark) .z-tfoot-wrapper.z-scroll-right td.z-sticky-right-first{border-left:1px solid var(--border)}.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: ["zCheckedChange", "zGroupValueChange", "zChange", "zGroupChange", "zControl"] }, { kind: "component", type: ZEmptyComponent, selector: "z-empty", inputs: ["class", "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", "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: ["zPageIndexChange", "zPageSizeChange", "zOnPageChange"] }, { kind: "component", type: ZTableFilterComponent, selector: "z-table-filter", inputs: ["zColumn", "zTable"] }, { 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"], 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: ZTableResizerDirective, selector: "[z-table-resizer],[zTableResizer]", inputs: ["zTableResizer"] }, { 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: ZTableCellConfigPipe, name: "zTableCellConfig" }, { kind: "pipe", type: ZTableCellOffsetPipe, name: "zTableCellOffset" }, { 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: ZTableHeaderPinPipe, name: "zTableHeaderPin" }, { kind: "pipe", type: ZTablePinningStylesPipe, name: "zTablePinningStyles" }, { kind: "pipe", type: ZTableRowPipe, name: "zTableRow" }, { kind: "pipe", type: ZTableSpanPipe, name: "zTableSpan" }, { kind: "pipe", type: ZTableFooterRenderPipe, name: "zTableFooterRender" }, { kind: "pipe", type: ZTableHeaderRenderPipe, name: "zTableHeaderRender" }, { kind: "pipe", type: ZTableBodyCellRenderPipe, name: "zTableBodyCellRender" }, { kind: "pipe", type: ZSafeHtmlPipe, name: "zSafeHtml" }, { kind: "pipe", type: TranslatePipe, name: "translate" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
3095
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.6", type: ZTableComponent, isStandalone: true, selector: "z-table", inputs: { 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-2" }, 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 }], exportAs: ["zTable"], ngImport: i0, template: "<!-- Toolbar: Search & Settings -->\n@if (zConfig().enableSearch || zConfig().enableSettings) {\n <div class=\"z-table-toolbar mb-2 flex items-center justify-between gap-4\">\n <!-- Search -->\n @if (zConfig().enableSearch) {\n <z-input\n class=\"w-64\"\n zSize=\"sm\"\n [zPlaceholder]=\"'i18n_z_ui_table_search' | translate\"\n [zSearch]=\"true\"\n [zDebounce]=\"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=\"z-table-container shadow-xs\"\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 <col [style.width]=\"'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 | zTableHeaderRender: headerGroup.headers : zConfig().columns;\n @if (rowSpan && shouldRender) {\n <th\n [attr.id]=\"header.column.id\"\n [ngStyle]=\"header.column | zTablePinningStyles: header : 'header' : table : zConfig().columns\"\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 | zTableHeaderPin: 'isLastLeftPinned' : zConfig().columns\"\n [class.z-sticky-right-first]=\"header | zTableHeaderPin: 'isFirstRightPinned' : zConfig().columns\"\n [class.z-sticky-right-last]=\"header | zTableHeaderPin: '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 zSize=\"sm\"\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 [zTableResizer]=\"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 (zLoading() || isProcessing()) {\n <!-- Loading State -->\n @if (zConfig().useSkeleton) {\n <!-- Skeleton Loading -->\n <div class=\"z-skeleton-state\">\n @for (i of skeletonRows(); track $index) {\n <div class=\"z-skeleton-row\" [style.height.px]=\"virtualRowHeight()\">\n <z-skeleton zType=\"bar\" zWidth=\"100%\" [zHeight]=\"virtualRowHeight() - 16 + 'px'\" />\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 class=\"z-virtual-row\"\n [style.height.px]=\"dynamicGroupHeights()[virtualItem.index] ?? groupSize() * virtualRowHeight()\"\n [style.transform]=\"'translateY(' + virtualItem.start + 'px)'\">\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]=\"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 @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 | zTableBodyCellRender: row.getVisibleCells() : zConfig().columns;\n @if (shouldRenderRowSpan && shouldRenderColSpan) {\n <td\n [ngStyle]=\"\n cell.column\n | zTablePinningStyles: cell : 'body' : row.getVisibleCells() : zConfig().columns\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 zSize=\"sm\"\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 <ng-container\n *flexRender=\"cell.column.columnDef.cell; props: cell.getContext(); let cellContent\">\n @if (cellContent | zTableIsTemplateRef) {\n <!-- TemplateRef rendering -->\n <div\n [ngClass]=\"cell | zTableCellConfig: zConfig().columns : 'contentClass'\"\n [ngStyle]=\"cell | zTableCellConfig: zConfig().columns : 'contentStyle'\">\n <ng-container\n *ngTemplateOutlet=\"cellContent; context: { $implicit: cell.getContext() }\" />\n </div>\n } @else if (cellContent | zTableHasIcon) {\n <!-- Icon syntax rendering -->\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 } @else {\n <!-- Default/innerHTML rendering -->\n <div\n class=\"z-table-cell-text\"\n z-tooltip\n [zContent]=\"\n (cell | zTableCellConfig: zConfig().columns : 'contentTooltip') || cellContent\n \"\n [innerHTML]=\"cellContent | zSafeHtml\"\n [ngClass]=\"cell | zTableCellConfig: zConfig().columns : 'contentClass'\"\n [ngStyle]=\"cell | zTableCellConfig: zConfig().columns : 'contentStyle'\"></div>\n }\n </ng-container>\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 = cell | zTableBodyCellRender: row.getVisibleCells() : zConfig().columns;\n @if (shouldRenderRowSpan && shouldRenderColSpan) {\n <td\n [ngStyle]=\"\n cell.column | zTablePinningStyles: cell : 'body' : row.getVisibleCells() : zConfig().columns\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 zSize=\"sm\"\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 <ng-container\n *flexRender=\"cell.column.columnDef.cell; props: cell.getContext(); let cellContent\">\n @if (cellContent | zTableIsTemplateRef) {\n <!-- TemplateRef rendering -->\n <div\n [ngClass]=\"cell | zTableCellConfig: zConfig().columns : 'contentClass'\"\n [ngStyle]=\"cell | zTableCellConfig: zConfig().columns : 'contentStyle'\">\n <ng-container\n *ngTemplateOutlet=\"cellContent; context: { $implicit: cell.getContext() }\" />\n </div>\n } @else if (cellContent | zTableHasIcon) {\n <!-- Icon syntax rendering -->\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 } @else {\n <!-- Default/innerHTML rendering -->\n <div\n class=\"z-table-cell-text\"\n z-tooltip\n [zContent]=\"\n (cell | zTableCellConfig: zConfig().columns : 'contentTooltip') || cellContent\n \"\n [innerHTML]=\"cellContent | zSafeHtml\"\n [ngClass]=\"cell | zTableCellConfig: zConfig().columns : 'contentClass'\"\n [ngStyle]=\"cell | zTableCellConfig: zConfig().columns : 'contentStyle'\"></div>\n }\n </ng-container>\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 | zTableFooterRender: footerGroup.headers : zConfig().columns;\n @if (rowSpan && shouldRender) {\n <th\n [ngStyle]=\"footer.column | zTablePinningStyles: footer : 'header' : table : zConfig().columns\"\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]=\"footer | zTableHeaderPin: 'isLastLeftPinned' : zConfig().columns\"\n [class.z-sticky-right-first]=\"footer | zTableHeaderPin: 'isFirstRightPinned' : zConfig().columns\"\n [class.z-sticky-right-last]=\"footer | zTableHeaderPin: 'isLastRightPinned' : zConfig().columns\"\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().enablePagination && 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=\"text-sm text-gray-500\">\n {{\n ('i18n_z_ui_table_total_rows' | translate).replace(\n '{total}',\n zConfig().totalCount?.toString() || totalRows.toString()\n )\n }}\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 || zLoading() || 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 zSize=\"sm\"\n [zChecked]=\"showHorizontalBorder()\"\n [zText]=\"'i18n_z_ui_table_horizontal_border' | translate\"\n (zChange)=\"showHorizontalBorder.set(!showHorizontalBorder())\" />\n <z-checkbox\n zSize=\"sm\"\n [zChecked]=\"showVerticalBorder()\"\n [zText]=\"'i18n_z_ui_table_vertical_border' | translate\"\n (zChange)=\"showVerticalBorder.set(!showVerticalBorder())\" />\n <z-checkbox\n zSize=\"sm\"\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 }}\n </span>\n @let parents = columnId | zTableColumnParents: zConfig().columns;\n @if (parents) {\n <span class=\"text-muted-foreground truncate text-[10px]\">({{ parents }})</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 <!-- 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 }}\n </span>\n @let pinnedParents = columnId | zTableColumnParents: zConfig().columns;\n @if (pinnedParents) {\n <span class=\"text-muted-foreground truncate text-[10px]\">({{ pinnedParents }})</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: [":host{display:flex;flex-direction:column;height:100%;--scrollbar-track-thickness: 7px;--scrollbar-track-color: transparent;--scrollbar-thumb-shape: 3px}.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:auto;overflow-y:hidden;scrollbar-width:none}.z-thead-wrapper::-webkit-scrollbar{display:none}.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-skeleton-state{display:flex;flex-direction:column;min-height:200px;height:100%;padding:0;animation:z-fade-in .2s ease-out}.z-skeleton-state .z-skeleton-row{display:flex;align-items:center;padding:0 8px;border-bottom:thin solid var(--border);box-sizing:border-box}.z-skeleton-state .z-skeleton-row:last-child{border-bottom:none}.z-skeleton-state .z-skeleton-row z-skeleton{display:flex;align-items:center;width:100%;height:auto!important}.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%,transparent)!important}tbody tr.z-selected:hover,tbody tr.z-selected:hover td{background-color:color-mix(in srgb,var(--primary) 20%,transparent)!important}tbody tr.z-indeterminate-selected,tbody tr.z-indeterminate-selected td{background-color:color-mix(in srgb,var(--primary) 10%,transparent)!important}tbody tr.z-indeterminate-selected:hover,tbody tr.z-indeterminate-selected:hover td{background-color:color-mix(in srgb,var(--primary) 15%,transparent)!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}.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:-30px;width:30px;pointer-events:none;box-shadow:inset 10px 0 8px -8px #0000001a;z-index:10;opacity:0;transition:opacity .2s ease}.z-thead-wrapper.z-scroll-left th.z-sticky-left-last:after,.z-thead-wrapper.z-scroll-left td.z-sticky-left-last:after,.z-tbody-wrapper.z-scroll-left th.z-sticky-left-last:after,.z-tbody-wrapper.z-scroll-left td.z-sticky-left-last:after,.z-tfoot-wrapper.z-scroll-left th.z-sticky-left-last:after,.z-tfoot-wrapper.z-scroll-left td.z-sticky-left-last:after{opacity:1}.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}.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:-30px;width:30px;pointer-events:none;box-shadow:inset -10px 0 8px -8px #0000001a;z-index:10;opacity:0;transition:opacity .2s ease}.z-thead-wrapper.z-scroll-right th.z-sticky-right-first,.z-thead-wrapper.z-scroll-right td.z-sticky-right-first,.z-tbody-wrapper.z-scroll-right th.z-sticky-right-first,.z-tbody-wrapper.z-scroll-right td.z-sticky-right-first,.z-tfoot-wrapper.z-scroll-right th.z-sticky-right-first,.z-tfoot-wrapper.z-scroll-right td.z-sticky-right-first{border-left:none}.z-thead-wrapper.z-scroll-right th.z-sticky-right-first:before,.z-thead-wrapper.z-scroll-right td.z-sticky-right-first:before,.z-tbody-wrapper.z-scroll-right th.z-sticky-right-first:before,.z-tbody-wrapper.z-scroll-right td.z-sticky-right-first:before,.z-tfoot-wrapper.z-scroll-right th.z-sticky-right-first:before,.z-tfoot-wrapper.z-scroll-right td.z-sticky-right-first:before{opacity:1}.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:auto;overflow-y:hidden;scrollbar-width:none}.z-tfoot-wrapper::-webkit-scrollbar{display:none}.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%,transparent);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}:host-context(.dark) .z-thead-wrapper.z-scroll-left th.z-sticky-left-last,:host-context(.dark) .z-thead-wrapper.z-scroll-left td.z-sticky-left-last,:host-context(.dark) .z-tbody-wrapper.z-scroll-left th.z-sticky-left-last,:host-context(.dark) .z-tbody-wrapper.z-scroll-left td.z-sticky-left-last,:host-context(.dark) .z-tfoot-wrapper.z-scroll-left th.z-sticky-left-last,:host-context(.dark) .z-tfoot-wrapper.z-scroll-left td.z-sticky-left-last{border-right:1px solid var(--border)}:host-context(.dark) .z-thead-wrapper.z-scroll-right th.z-sticky-right-first,:host-context(.dark) .z-thead-wrapper.z-scroll-right td.z-sticky-right-first,:host-context(.dark) .z-tbody-wrapper.z-scroll-right th.z-sticky-right-first,:host-context(.dark) .z-tbody-wrapper.z-scroll-right td.z-sticky-right-first,:host-context(.dark) .z-tfoot-wrapper.z-scroll-right th.z-sticky-right-first,:host-context(.dark) .z-tfoot-wrapper.z-scroll-right td.z-sticky-right-first{border-left:1px solid var(--border)}.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: ["zCheckedChange", "zGroupValueChange", "zChange", "zGroupChange", "zControl"] }, { kind: "component", type: ZEmptyComponent, selector: "z-empty", inputs: ["class", "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", "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: ["zPageIndexChange", "zPageSizeChange", "zOnPageChange"] }, { kind: "component", type: ZTableFilterComponent, selector: "z-table-filter", inputs: ["zColumn", "zTable"] }, { 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"], 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: ZTableResizerDirective, selector: "[z-table-resizer],[zTableResizer]", inputs: ["zTableResizer"] }, { 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: ZTableCellConfigPipe, name: "zTableCellConfig" }, { kind: "pipe", type: ZTableCellOffsetPipe, name: "zTableCellOffset" }, { 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: ZTableHeaderPinPipe, name: "zTableHeaderPin" }, { kind: "pipe", type: ZTablePinningStylesPipe, name: "zTablePinningStyles" }, { kind: "pipe", type: ZTableRowPipe, name: "zTableRow" }, { kind: "pipe", type: ZTableSpanPipe, name: "zTableSpan" }, { kind: "pipe", type: ZTableFooterRenderPipe, name: "zTableFooterRender" }, { kind: "pipe", type: ZTableHeaderRenderPipe, name: "zTableHeaderRender" }, { kind: "pipe", type: ZTableBodyCellRenderPipe, name: "zTableBodyCellRender" }, { kind: "pipe", type: ZSafeHtmlPipe, name: "zSafeHtml" }, { kind: "pipe", type: TranslatePipe, name: "translate" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
3118
3096
|
}
|
|
3119
3097
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: ZTableComponent, decorators: [{
|
|
3120
3098
|
type: Component,
|
|
@@ -3163,7 +3141,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImpor
|
|
|
3163
3141
|
TranslatePipe,
|
|
3164
3142
|
], standalone: true, providers: [TranslatePipe], changeDetection: ChangeDetectionStrategy.OnPush, host: {
|
|
3165
3143
|
class: 'z-table block relative py-2',
|
|
3166
|
-
}, exportAs: 'zTable', template: "<!-- Toolbar: Search & Settings -->\n@if (zConfig().enableSearch || zConfig().enableSettings) {\n <div class=\"z-table-toolbar mb-2 flex items-center justify-between gap-4\">\n <!-- Search -->\n @if (zConfig().enableSearch) {\n <z-input\n class=\"w-64\"\n zSize=\"sm\"\n [zPlaceholder]=\"'i18n_z_ui_table_search' | translate\"\n [zSearch]=\"true\"\n [zDebounce]=\"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=\"z-table-container shadow-xs\"\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 <col [style.width]=\"'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 | zTableHeaderRender: headerGroup.headers : zConfig().columns;\n @if (rowSpan && shouldRender) {\n <th\n [attr.id]=\"header.column.id\"\n [ngStyle]=\"header.column | zTablePinningStyles: header : 'header' : table : zConfig().columns\"\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 | zTableHeaderPin: 'isLastLeftPinned' : zConfig().columns\"\n [class.z-sticky-right-first]=\"header | zTableHeaderPin: 'isFirstRightPinned' : zConfig().columns\"\n [class.z-sticky-right-last]=\"header | zTableHeaderPin: '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 zSize=\"sm\"\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 [zTableResizer]=\"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 (zLoading() || isProcessing()) {\n <!-- Loading State -->\n @if (zConfig().useSkeleton) {\n <!-- Skeleton Loading -->\n <div class=\"z-skeleton-state\">\n @for (i of skeletonRows(); track $index) {\n <div class=\"z-skeleton-row\" [style.height.px]=\"virtualRowHeight()\">\n <z-skeleton zType=\"bar\" zWidth=\"100%\" [zHeight]=\"virtualRowHeight() - 16 + 'px'\" />\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 class=\"z-virtual-row\"\n [style.height.px]=\"dynamicGroupHeights()[virtualItem.index] ?? groupSize() * virtualRowHeight()\"\n [style.transform]=\"'translateY(' + virtualItem.start + 'px)'\">\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]=\"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 @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 | zTableBodyCellRender: row.getVisibleCells() : zConfig().columns;\n @if (shouldRenderRowSpan && shouldRenderColSpan) {\n <td\n [ngStyle]=\"\n cell.column\n | zTablePinningStyles: cell : 'body' : row.getVisibleCells() : zConfig().columns\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 zSize=\"sm\"\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 <ng-container\n *flexRender=\"cell.column.columnDef.cell; props: cell.getContext(); let cellContent\">\n @if (cellContent | zTableIsTemplateRef) {\n <!-- TemplateRef rendering -->\n <div\n [ngClass]=\"cell | zTableCellConfig: zConfig().columns : 'contentClass'\"\n [ngStyle]=\"cell | zTableCellConfig: zConfig().columns : 'contentStyle'\">\n <ng-container\n *ngTemplateOutlet=\"cellContent; context: { $implicit: cell.getContext() }\" />\n </div>\n } @else if (cellContent | zTableHasIcon) {\n <!-- Icon syntax rendering -->\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 } @else {\n <!-- Default/innerHTML rendering -->\n <div\n class=\"z-table-cell-text\"\n z-tooltip\n [zContent]=\"\n (cell | zTableCellConfig: zConfig().columns : 'contentTooltip') || cellContent\n \"\n [innerHTML]=\"cellContent | zSafeHtml\"\n [ngClass]=\"cell | zTableCellConfig: zConfig().columns : 'contentClass'\"\n [ngStyle]=\"cell | zTableCellConfig: zConfig().columns : 'contentStyle'\"></div>\n }\n </ng-container>\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 = cell | zTableBodyCellRender: row.getVisibleCells() : zConfig().columns;\n @if (shouldRenderRowSpan && shouldRenderColSpan) {\n <td\n [ngStyle]=\"\n cell.column | zTablePinningStyles: cell : 'body' : row.getVisibleCells() : zConfig().columns\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 zSize=\"sm\"\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 <ng-container\n *flexRender=\"cell.column.columnDef.cell; props: cell.getContext(); let cellContent\">\n @if (cellContent | zTableIsTemplateRef) {\n <!-- TemplateRef rendering -->\n <div\n [ngClass]=\"cell | zTableCellConfig: zConfig().columns : 'contentClass'\"\n [ngStyle]=\"cell | zTableCellConfig: zConfig().columns : 'contentStyle'\">\n <ng-container\n *ngTemplateOutlet=\"cellContent; context: { $implicit: cell.getContext() }\" />\n </div>\n } @else if (cellContent | zTableHasIcon) {\n <!-- Icon syntax rendering -->\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 } @else {\n <!-- Default/innerHTML rendering -->\n <div\n class=\"z-table-cell-text\"\n z-tooltip\n [zContent]=\"\n (cell | zTableCellConfig: zConfig().columns : 'contentTooltip') || cellContent\n \"\n [innerHTML]=\"cellContent | zSafeHtml\"\n [ngClass]=\"cell | zTableCellConfig: zConfig().columns : 'contentClass'\"\n [ngStyle]=\"cell | zTableCellConfig: zConfig().columns : 'contentStyle'\"></div>\n }\n </ng-container>\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 | zTableFooterRender: footerGroup.headers : zConfig().columns;\n @if (rowSpan && shouldRender) {\n <th\n [ngStyle]=\"footer.column | zTablePinningStyles: footer : 'header' : table : zConfig().columns\"\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]=\"footer | zTableHeaderPin: 'isLastLeftPinned' : zConfig().columns\"\n [class.z-sticky-right-first]=\"footer | zTableHeaderPin: 'isFirstRightPinned' : zConfig().columns\"\n [class.z-sticky-right-last]=\"footer | zTableHeaderPin: 'isLastRightPinned' : zConfig().columns\"\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().enablePagination && 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=\"text-sm text-gray-500\">\n {{\n ('i18n_z_ui_table_total_rows' | translate).replace(\n '{total}',\n zConfig().totalCount?.toString() || totalRows.toString()\n )\n }}\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 || zLoading() || 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 zSize=\"sm\"\n [zChecked]=\"showHorizontalBorder()\"\n [zText]=\"'i18n_z_ui_table_horizontal_border' | translate\"\n (zChange)=\"showHorizontalBorder.set(!showHorizontalBorder())\" />\n <z-checkbox\n zSize=\"sm\"\n [zChecked]=\"showVerticalBorder()\"\n [zText]=\"'i18n_z_ui_table_vertical_border' | translate\"\n (zChange)=\"showVerticalBorder.set(!showVerticalBorder())\" />\n <z-checkbox\n zSize=\"sm\"\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 }}\n </span>\n @let parents = columnId | zTableColumnParents: zConfig().columns;\n @if (parents) {\n <span class=\"text-muted-foreground truncate text-[10px]\">({{ parents }})</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 <!-- 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 }}\n </span>\n @let pinnedParents = columnId | zTableColumnParents: zConfig().columns;\n @if (pinnedParents) {\n <span class=\"text-muted-foreground truncate text-[10px]\">({{ pinnedParents }})</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: [":host{display:flex;flex-direction:column;height:100%;--scrollbar-track-thickness: 7px;--scrollbar-track-color: transparent;--scrollbar-thumb-shape: 3px}.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:auto;overflow-y:hidden;scrollbar-width:none}.z-thead-wrapper::-webkit-scrollbar{display:none}.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-skeleton-state{display:flex;flex-direction:column;min-height:200px;height:100%;padding:0;animation:z-fade-in .2s ease-out}.z-skeleton-state .z-skeleton-row{display:flex;align-items:center;padding:0 8px;border-bottom:thin solid var(--border);box-sizing:border-box}.z-skeleton-state .z-skeleton-row:last-child{border-bottom:none}.z-skeleton-state .z-skeleton-row z-skeleton{display:flex;align-items:center;width:100%;height:auto!important}.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%,transparent)!important}tbody tr.z-selected:hover,tbody tr.z-selected:hover td{background-color:color-mix(in srgb,var(--primary) 20%,transparent)!important}tbody tr.z-indeterminate-selected,tbody tr.z-indeterminate-selected td{background-color:color-mix(in srgb,var(--primary) 10%,transparent)!important}tbody tr.z-indeterminate-selected:hover,tbody tr.z-indeterminate-selected:hover td{background-color:color-mix(in srgb,var(--primary) 15%,transparent)!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}.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:-30px;width:30px;pointer-events:none;box-shadow:inset 10px 0 8px -8px #0000001a;z-index:10;opacity:0;transition:opacity .2s ease}.z-thead-wrapper.z-scroll-left th.z-sticky-left-last:after,.z-thead-wrapper.z-scroll-left td.z-sticky-left-last:after,.z-tbody-wrapper.z-scroll-left th.z-sticky-left-last:after,.z-tbody-wrapper.z-scroll-left td.z-sticky-left-last:after,.z-tfoot-wrapper.z-scroll-left th.z-sticky-left-last:after,.z-tfoot-wrapper.z-scroll-left td.z-sticky-left-last:after{opacity:1}.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}.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:-30px;width:30px;pointer-events:none;box-shadow:inset -10px 0 8px -8px #0000001a;z-index:10;opacity:0;transition:opacity .2s ease}.z-thead-wrapper.z-scroll-right th.z-sticky-right-first,.z-thead-wrapper.z-scroll-right td.z-sticky-right-first,.z-tbody-wrapper.z-scroll-right th.z-sticky-right-first,.z-tbody-wrapper.z-scroll-right td.z-sticky-right-first,.z-tfoot-wrapper.z-scroll-right th.z-sticky-right-first,.z-tfoot-wrapper.z-scroll-right td.z-sticky-right-first{border-left:none}.z-thead-wrapper.z-scroll-right th.z-sticky-right-first:before,.z-thead-wrapper.z-scroll-right td.z-sticky-right-first:before,.z-tbody-wrapper.z-scroll-right th.z-sticky-right-first:before,.z-tbody-wrapper.z-scroll-right td.z-sticky-right-first:before,.z-tfoot-wrapper.z-scroll-right th.z-sticky-right-first:before,.z-tfoot-wrapper.z-scroll-right td.z-sticky-right-first:before{opacity:1}.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:auto;overflow-y:hidden;scrollbar-width:none}.z-tfoot-wrapper::-webkit-scrollbar{display:none}.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}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%,transparent);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}:host-context(.dark) .z-thead-wrapper.z-scroll-left th.z-sticky-left-last,:host-context(.dark) .z-thead-wrapper.z-scroll-left td.z-sticky-left-last,:host-context(.dark) .z-tbody-wrapper.z-scroll-left th.z-sticky-left-last,:host-context(.dark) .z-tbody-wrapper.z-scroll-left td.z-sticky-left-last,:host-context(.dark) .z-tfoot-wrapper.z-scroll-left th.z-sticky-left-last,:host-context(.dark) .z-tfoot-wrapper.z-scroll-left td.z-sticky-left-last{border-right:1px solid var(--border)}:host-context(.dark) .z-thead-wrapper.z-scroll-right th.z-sticky-right-first,:host-context(.dark) .z-thead-wrapper.z-scroll-right td.z-sticky-right-first,:host-context(.dark) .z-tbody-wrapper.z-scroll-right th.z-sticky-right-first,:host-context(.dark) .z-tbody-wrapper.z-scroll-right td.z-sticky-right-first,:host-context(.dark) .z-tfoot-wrapper.z-scroll-right th.z-sticky-right-first,:host-context(.dark) .z-tfoot-wrapper.z-scroll-right td.z-sticky-right-first{border-left:1px solid var(--border)}.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"] }]
|
|
3144
|
+
}, exportAs: 'zTable', template: "<!-- Toolbar: Search & Settings -->\n@if (zConfig().enableSearch || zConfig().enableSettings) {\n <div class=\"z-table-toolbar mb-2 flex items-center justify-between gap-4\">\n <!-- Search -->\n @if (zConfig().enableSearch) {\n <z-input\n class=\"w-64\"\n zSize=\"sm\"\n [zPlaceholder]=\"'i18n_z_ui_table_search' | translate\"\n [zSearch]=\"true\"\n [zDebounce]=\"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=\"z-table-container shadow-xs\"\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 <col [style.width]=\"'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 | zTableHeaderRender: headerGroup.headers : zConfig().columns;\n @if (rowSpan && shouldRender) {\n <th\n [attr.id]=\"header.column.id\"\n [ngStyle]=\"header.column | zTablePinningStyles: header : 'header' : table : zConfig().columns\"\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 | zTableHeaderPin: 'isLastLeftPinned' : zConfig().columns\"\n [class.z-sticky-right-first]=\"header | zTableHeaderPin: 'isFirstRightPinned' : zConfig().columns\"\n [class.z-sticky-right-last]=\"header | zTableHeaderPin: '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 zSize=\"sm\"\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 [zTableResizer]=\"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 (zLoading() || isProcessing()) {\n <!-- Loading State -->\n @if (zConfig().useSkeleton) {\n <!-- Skeleton Loading -->\n <div class=\"z-skeleton-state\">\n @for (i of skeletonRows(); track $index) {\n <div class=\"z-skeleton-row\" [style.height.px]=\"virtualRowHeight()\">\n <z-skeleton zType=\"bar\" zWidth=\"100%\" [zHeight]=\"virtualRowHeight() - 16 + 'px'\" />\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 class=\"z-virtual-row\"\n [style.height.px]=\"dynamicGroupHeights()[virtualItem.index] ?? groupSize() * virtualRowHeight()\"\n [style.transform]=\"'translateY(' + virtualItem.start + 'px)'\">\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]=\"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 @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 | zTableBodyCellRender: row.getVisibleCells() : zConfig().columns;\n @if (shouldRenderRowSpan && shouldRenderColSpan) {\n <td\n [ngStyle]=\"\n cell.column\n | zTablePinningStyles: cell : 'body' : row.getVisibleCells() : zConfig().columns\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 zSize=\"sm\"\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 <ng-container\n *flexRender=\"cell.column.columnDef.cell; props: cell.getContext(); let cellContent\">\n @if (cellContent | zTableIsTemplateRef) {\n <!-- TemplateRef rendering -->\n <div\n [ngClass]=\"cell | zTableCellConfig: zConfig().columns : 'contentClass'\"\n [ngStyle]=\"cell | zTableCellConfig: zConfig().columns : 'contentStyle'\">\n <ng-container\n *ngTemplateOutlet=\"cellContent; context: { $implicit: cell.getContext() }\" />\n </div>\n } @else if (cellContent | zTableHasIcon) {\n <!-- Icon syntax rendering -->\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 } @else {\n <!-- Default/innerHTML rendering -->\n <div\n class=\"z-table-cell-text\"\n z-tooltip\n [zContent]=\"\n (cell | zTableCellConfig: zConfig().columns : 'contentTooltip') || cellContent\n \"\n [innerHTML]=\"cellContent | zSafeHtml\"\n [ngClass]=\"cell | zTableCellConfig: zConfig().columns : 'contentClass'\"\n [ngStyle]=\"cell | zTableCellConfig: zConfig().columns : 'contentStyle'\"></div>\n }\n </ng-container>\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 = cell | zTableBodyCellRender: row.getVisibleCells() : zConfig().columns;\n @if (shouldRenderRowSpan && shouldRenderColSpan) {\n <td\n [ngStyle]=\"\n cell.column | zTablePinningStyles: cell : 'body' : row.getVisibleCells() : zConfig().columns\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 zSize=\"sm\"\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 <ng-container\n *flexRender=\"cell.column.columnDef.cell; props: cell.getContext(); let cellContent\">\n @if (cellContent | zTableIsTemplateRef) {\n <!-- TemplateRef rendering -->\n <div\n [ngClass]=\"cell | zTableCellConfig: zConfig().columns : 'contentClass'\"\n [ngStyle]=\"cell | zTableCellConfig: zConfig().columns : 'contentStyle'\">\n <ng-container\n *ngTemplateOutlet=\"cellContent; context: { $implicit: cell.getContext() }\" />\n </div>\n } @else if (cellContent | zTableHasIcon) {\n <!-- Icon syntax rendering -->\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 } @else {\n <!-- Default/innerHTML rendering -->\n <div\n class=\"z-table-cell-text\"\n z-tooltip\n [zContent]=\"\n (cell | zTableCellConfig: zConfig().columns : 'contentTooltip') || cellContent\n \"\n [innerHTML]=\"cellContent | zSafeHtml\"\n [ngClass]=\"cell | zTableCellConfig: zConfig().columns : 'contentClass'\"\n [ngStyle]=\"cell | zTableCellConfig: zConfig().columns : 'contentStyle'\"></div>\n }\n </ng-container>\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 | zTableFooterRender: footerGroup.headers : zConfig().columns;\n @if (rowSpan && shouldRender) {\n <th\n [ngStyle]=\"footer.column | zTablePinningStyles: footer : 'header' : table : zConfig().columns\"\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]=\"footer | zTableHeaderPin: 'isLastLeftPinned' : zConfig().columns\"\n [class.z-sticky-right-first]=\"footer | zTableHeaderPin: 'isFirstRightPinned' : zConfig().columns\"\n [class.z-sticky-right-last]=\"footer | zTableHeaderPin: 'isLastRightPinned' : zConfig().columns\"\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().enablePagination && 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=\"text-sm text-gray-500\">\n {{\n ('i18n_z_ui_table_total_rows' | translate).replace(\n '{total}',\n zConfig().totalCount?.toString() || totalRows.toString()\n )\n }}\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 || zLoading() || 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 zSize=\"sm\"\n [zChecked]=\"showHorizontalBorder()\"\n [zText]=\"'i18n_z_ui_table_horizontal_border' | translate\"\n (zChange)=\"showHorizontalBorder.set(!showHorizontalBorder())\" />\n <z-checkbox\n zSize=\"sm\"\n [zChecked]=\"showVerticalBorder()\"\n [zText]=\"'i18n_z_ui_table_vertical_border' | translate\"\n (zChange)=\"showVerticalBorder.set(!showVerticalBorder())\" />\n <z-checkbox\n zSize=\"sm\"\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 }}\n </span>\n @let parents = columnId | zTableColumnParents: zConfig().columns;\n @if (parents) {\n <span class=\"text-muted-foreground truncate text-[10px]\">({{ parents }})</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 <!-- 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 }}\n </span>\n @let pinnedParents = columnId | zTableColumnParents: zConfig().columns;\n @if (pinnedParents) {\n <span class=\"text-muted-foreground truncate text-[10px]\">({{ pinnedParents }})</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: [":host{display:flex;flex-direction:column;height:100%;--scrollbar-track-thickness: 7px;--scrollbar-track-color: transparent;--scrollbar-thumb-shape: 3px}.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:auto;overflow-y:hidden;scrollbar-width:none}.z-thead-wrapper::-webkit-scrollbar{display:none}.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-skeleton-state{display:flex;flex-direction:column;min-height:200px;height:100%;padding:0;animation:z-fade-in .2s ease-out}.z-skeleton-state .z-skeleton-row{display:flex;align-items:center;padding:0 8px;border-bottom:thin solid var(--border);box-sizing:border-box}.z-skeleton-state .z-skeleton-row:last-child{border-bottom:none}.z-skeleton-state .z-skeleton-row z-skeleton{display:flex;align-items:center;width:100%;height:auto!important}.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%,transparent)!important}tbody tr.z-selected:hover,tbody tr.z-selected:hover td{background-color:color-mix(in srgb,var(--primary) 20%,transparent)!important}tbody tr.z-indeterminate-selected,tbody tr.z-indeterminate-selected td{background-color:color-mix(in srgb,var(--primary) 10%,transparent)!important}tbody tr.z-indeterminate-selected:hover,tbody tr.z-indeterminate-selected:hover td{background-color:color-mix(in srgb,var(--primary) 15%,transparent)!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}.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:-30px;width:30px;pointer-events:none;box-shadow:inset 10px 0 8px -8px #0000001a;z-index:10;opacity:0;transition:opacity .2s ease}.z-thead-wrapper.z-scroll-left th.z-sticky-left-last:after,.z-thead-wrapper.z-scroll-left td.z-sticky-left-last:after,.z-tbody-wrapper.z-scroll-left th.z-sticky-left-last:after,.z-tbody-wrapper.z-scroll-left td.z-sticky-left-last:after,.z-tfoot-wrapper.z-scroll-left th.z-sticky-left-last:after,.z-tfoot-wrapper.z-scroll-left td.z-sticky-left-last:after{opacity:1}.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}.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:-30px;width:30px;pointer-events:none;box-shadow:inset -10px 0 8px -8px #0000001a;z-index:10;opacity:0;transition:opacity .2s ease}.z-thead-wrapper.z-scroll-right th.z-sticky-right-first,.z-thead-wrapper.z-scroll-right td.z-sticky-right-first,.z-tbody-wrapper.z-scroll-right th.z-sticky-right-first,.z-tbody-wrapper.z-scroll-right td.z-sticky-right-first,.z-tfoot-wrapper.z-scroll-right th.z-sticky-right-first,.z-tfoot-wrapper.z-scroll-right td.z-sticky-right-first{border-left:none}.z-thead-wrapper.z-scroll-right th.z-sticky-right-first:before,.z-thead-wrapper.z-scroll-right td.z-sticky-right-first:before,.z-tbody-wrapper.z-scroll-right th.z-sticky-right-first:before,.z-tbody-wrapper.z-scroll-right td.z-sticky-right-first:before,.z-tfoot-wrapper.z-scroll-right th.z-sticky-right-first:before,.z-tfoot-wrapper.z-scroll-right td.z-sticky-right-first:before{opacity:1}.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:auto;overflow-y:hidden;scrollbar-width:none}.z-tfoot-wrapper::-webkit-scrollbar{display:none}.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%,transparent);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}:host-context(.dark) .z-thead-wrapper.z-scroll-left th.z-sticky-left-last,:host-context(.dark) .z-thead-wrapper.z-scroll-left td.z-sticky-left-last,:host-context(.dark) .z-tbody-wrapper.z-scroll-left th.z-sticky-left-last,:host-context(.dark) .z-tbody-wrapper.z-scroll-left td.z-sticky-left-last,:host-context(.dark) .z-tfoot-wrapper.z-scroll-left th.z-sticky-left-last,:host-context(.dark) .z-tfoot-wrapper.z-scroll-left td.z-sticky-left-last{border-right:1px solid var(--border)}:host-context(.dark) .z-thead-wrapper.z-scroll-right th.z-sticky-right-first,:host-context(.dark) .z-thead-wrapper.z-scroll-right td.z-sticky-right-first,:host-context(.dark) .z-tbody-wrapper.z-scroll-right th.z-sticky-right-first,:host-context(.dark) .z-tbody-wrapper.z-scroll-right td.z-sticky-right-first,:host-context(.dark) .z-tfoot-wrapper.z-scroll-right th.z-sticky-right-first,:host-context(.dark) .z-tfoot-wrapper.z-scroll-right td.z-sticky-right-first{border-left:1px solid var(--border)}.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"] }]
|
|
3167
3145
|
}], ctorParameters: () => [], propDecorators: { 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 }] }], zChange: [{ type: i0.Output, args: ["zChange"] }], zControl: [{ type: i0.Output, args: ["zControl"] }], 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 }] }] } });
|
|
3168
3146
|
|
|
3169
3147
|
/**
|