@shival99/z-ui 1.1.14 → 1.1.16
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/fesm2022/shival99-z-ui-components-z-calendar.mjs +13 -6
- package/fesm2022/shival99-z-ui-components-z-calendar.mjs.map +1 -1
- package/fesm2022/shival99-z-ui-components-z-dropdown-menu.mjs +4 -3
- package/fesm2022/shival99-z-ui-components-z-dropdown-menu.mjs.map +1 -1
- package/fesm2022/shival99-z-ui-components-z-empty.mjs +2 -2
- package/fesm2022/shival99-z-ui-components-z-empty.mjs.map +1 -1
- package/fesm2022/shival99-z-ui-components-z-table.mjs +860 -494
- package/fesm2022/shival99-z-ui-components-z-table.mjs.map +1 -1
- package/fesm2022/shival99-z-ui-components-z-toast.mjs +1 -1
- package/fesm2022/shival99-z-ui-components-z-toast.mjs.map +1 -1
- package/fesm2022/shival99-z-ui-i18n.mjs +4 -0
- package/fesm2022/shival99-z-ui-i18n.mjs.map +1 -1
- package/fesm2022/shival99-z-ui-services.mjs.map +1 -1
- package/package.json +2 -2
- package/types/shival99-z-ui-components-z-dropdown-menu.d.ts +2 -1
- package/types/shival99-z-ui-components-z-table.d.ts +106 -10
- package/types/shival99-z-ui-components-z-toast.d.ts +1 -1
|
@@ -2,7 +2,7 @@ import { moveItemInArray, CdkDropList, CdkDrag } from '@angular/cdk/drag-drop';
|
|
|
2
2
|
import { ScrollingModule } from '@angular/cdk/scrolling';
|
|
3
3
|
import { NgClass, NgStyle, NgTemplateOutlet } from '@angular/common';
|
|
4
4
|
import * as i0 from '@angular/core';
|
|
5
|
-
import { input,
|
|
5
|
+
import { input, output, computed, ChangeDetectionStrategy, Component, inject, ElementRef, Renderer2, Directive, NgZone, Pipe, TemplateRef, DestroyRef, signal, viewChild, untracked, effect, afterNextRender } from '@angular/core';
|
|
6
6
|
import { TranslateService, TranslatePipe } from '@ngx-translate/core';
|
|
7
7
|
import { injectVirtualizer } from '@shival99/angular-virtual';
|
|
8
8
|
import { ZButtonComponent } from '@shival99/z-ui/components/z-button';
|
|
@@ -21,94 +21,470 @@ import { ZCacheService } from '@shival99/z-ui/services';
|
|
|
21
21
|
import { createAngularTable, getFacetedMinMaxValues, getFacetedUniqueValues, getFacetedRowModel, getPaginationRowModel, getSortedRowModel, getFilteredRowModel, getExpandedRowModel, getCoreRowModel, FlexRenderDirective } from '@tanstack/angular-table';
|
|
22
22
|
import { NgScrollbar } from 'ngx-scrollbar';
|
|
23
23
|
import { explicitEffect } from 'ngxtension/explicit-effect';
|
|
24
|
+
import { ZDropdownMenuComponent } from '@shival99/z-ui/components/z-dropdown-menu';
|
|
24
25
|
import { toSignal } from '@angular/core/rxjs-interop';
|
|
25
26
|
import * as i1 from '@angular/forms';
|
|
26
27
|
import { FormsModule } from '@angular/forms';
|
|
28
|
+
import { ZCalendarComponent } from '@shival99/z-ui/components/z-calendar';
|
|
27
29
|
import { ZSelectComponent } from '@shival99/z-ui/components/z-select';
|
|
28
30
|
import { startWith } from 'rxjs';
|
|
29
31
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
32
|
+
const DEFAULT_MAX_VISIBLE = 3;
|
|
33
|
+
const DEFAULT_DROPDOWN_BUTTON_SIZE = 'sm';
|
|
34
|
+
class ZTableActionsComponent {
|
|
35
|
+
zConfig = input.required(...(ngDevMode ? [{ debugName: "zConfig" }] : []));
|
|
36
|
+
zRow = input.required(...(ngDevMode ? [{ debugName: "zRow" }] : []));
|
|
37
|
+
zRowId = input.required(...(ngDevMode ? [{ debugName: "zRowId" }] : []));
|
|
38
|
+
zDropdownButtonSize = input(DEFAULT_DROPDOWN_BUTTON_SIZE, ...(ngDevMode ? [{ debugName: "zDropdownButtonSize" }] : []));
|
|
39
|
+
zActionClick = output();
|
|
40
|
+
allActions = computed(() => {
|
|
41
|
+
const config = this.zConfig();
|
|
42
|
+
const row = this.zRow();
|
|
43
|
+
return config.actions.filter(action => {
|
|
44
|
+
if (typeof action.hidden === 'function') {
|
|
45
|
+
return !action.hidden(row);
|
|
46
|
+
}
|
|
47
|
+
return !action.hidden;
|
|
48
|
+
});
|
|
49
|
+
}, ...(ngDevMode ? [{ debugName: "allActions" }] : []));
|
|
50
|
+
shouldShowAsButtons = computed(() => {
|
|
51
|
+
const actions = this.allActions();
|
|
52
|
+
const maxVisible = this.zConfig().maxVisible ?? DEFAULT_MAX_VISIBLE;
|
|
53
|
+
return actions.length <= maxVisible;
|
|
54
|
+
}, ...(ngDevMode ? [{ debugName: "shouldShowAsButtons" }] : []));
|
|
55
|
+
actionStates = computed(() => {
|
|
56
|
+
const row = this.zRow();
|
|
57
|
+
const actions = this.allActions();
|
|
58
|
+
const states = {};
|
|
59
|
+
for (const action of actions) {
|
|
60
|
+
const isHidden = typeof action.hidden === 'function' ? action.hidden(row) : (action.hidden ?? false);
|
|
61
|
+
const isDisabled = typeof action.disabled === 'function' ? action.disabled(row) : (action.disabled ?? false);
|
|
62
|
+
const classes = ['text-muted-foreground hover:text-foreground cursor-pointer rounded p-1 transition-colors'];
|
|
63
|
+
if (action.danger) {
|
|
64
|
+
classes.push('z-action-danger');
|
|
65
|
+
}
|
|
66
|
+
if (action.class) {
|
|
67
|
+
classes.push(action.class);
|
|
68
|
+
}
|
|
69
|
+
states[action.key] = {
|
|
70
|
+
visible: !isHidden,
|
|
71
|
+
disabled: isDisabled,
|
|
72
|
+
buttonClass: classes.join(' '),
|
|
73
|
+
tooltip: action.tooltip ?? '',
|
|
74
|
+
};
|
|
45
75
|
}
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
76
|
+
return states;
|
|
77
|
+
}, ...(ngDevMode ? [{ debugName: "actionStates" }] : []));
|
|
78
|
+
dropdownItems = computed(() => {
|
|
79
|
+
const row = this.zRow();
|
|
80
|
+
return this.allActions().map(action => {
|
|
81
|
+
const isDisabled = typeof action.disabled === 'function' ? action.disabled(row) : (action.disabled ?? false);
|
|
82
|
+
return {
|
|
83
|
+
label: action.label ?? action.key,
|
|
84
|
+
icon: action.icon,
|
|
85
|
+
disabled: isDisabled,
|
|
86
|
+
class: action.danger ? 'text-destructive hover:text-destructive hover:bg-destructive/10' : '',
|
|
87
|
+
divide: action.divide,
|
|
88
|
+
onClick: () => this._emitActionClick(action),
|
|
89
|
+
};
|
|
49
90
|
});
|
|
50
|
-
|
|
51
|
-
|
|
91
|
+
}, ...(ngDevMode ? [{ debugName: "dropdownItems" }] : []));
|
|
92
|
+
_onActionClick(action, event) {
|
|
93
|
+
event.stopPropagation();
|
|
94
|
+
const states = this.actionStates();
|
|
95
|
+
if (states[action.key]?.disabled) {
|
|
52
96
|
return;
|
|
53
97
|
}
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
98
|
+
this._emitActionClick(action);
|
|
99
|
+
}
|
|
100
|
+
_onDropdownItemClick(item) {
|
|
101
|
+
const action = this.allActions().find(a => (a.label ?? a.key) === item.label);
|
|
102
|
+
if (action) {
|
|
103
|
+
this._emitActionClick(action);
|
|
58
104
|
}
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
105
|
+
}
|
|
106
|
+
_emitActionClick(action) {
|
|
107
|
+
this.zActionClick.emit({
|
|
108
|
+
key: action.key,
|
|
109
|
+
row: this.zRow(),
|
|
110
|
+
rowId: this.zRowId(),
|
|
111
|
+
action,
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: ZTableActionsComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
115
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.6", type: ZTableActionsComponent, isStandalone: true, selector: "z-table-actions", inputs: { zConfig: { classPropertyName: "zConfig", publicName: "zConfig", isSignal: true, isRequired: true, transformFunction: null }, zRow: { classPropertyName: "zRow", publicName: "zRow", isSignal: true, isRequired: true, transformFunction: null }, zRowId: { classPropertyName: "zRowId", publicName: "zRowId", isSignal: true, isRequired: true, transformFunction: null }, zDropdownButtonSize: { classPropertyName: "zDropdownButtonSize", publicName: "zDropdownButtonSize", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { zActionClick: "zActionClick" }, ngImport: i0, template: `
|
|
116
|
+
<div class="z-table-actions flex items-center justify-center gap-1">
|
|
117
|
+
@if (shouldShowAsButtons()) {
|
|
118
|
+
@for (action of allActions(); track action.key) {
|
|
119
|
+
@if (actionStates()[action.key]?.visible) {
|
|
120
|
+
<button
|
|
121
|
+
z-button
|
|
122
|
+
[zType]="action.type ?? 'outline'"
|
|
123
|
+
[zSize]="action.size ?? 'sm'"
|
|
124
|
+
[zDisabled]="actionStates()[action.key]?.disabled ?? false"
|
|
125
|
+
[class]="actionStates()[action.key]?.buttonClass ?? ''"
|
|
126
|
+
[attr.title]="action.tooltip"
|
|
127
|
+
z-tooltip
|
|
128
|
+
[zContent]="actionStates()[action.key]?.tooltip ?? ''"
|
|
129
|
+
(click)="_onActionClick(action, $event)"
|
|
130
|
+
>
|
|
131
|
+
@if (action.icon) {
|
|
132
|
+
<z-icon [zType]="action.icon" zSize="14" />
|
|
133
|
+
}
|
|
134
|
+
@if (action.label && !action.icon) {
|
|
135
|
+
{{ action.label }}
|
|
136
|
+
}
|
|
137
|
+
</button>
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
} @else {
|
|
141
|
+
<z-dropdown-menu
|
|
142
|
+
[zItems]="dropdownItems()"
|
|
143
|
+
zIcon="lucideMenu"
|
|
144
|
+
zButtonType="outline"
|
|
145
|
+
zPosition="bottom-right"
|
|
146
|
+
[zButtonSize]="zDropdownButtonSize()"
|
|
147
|
+
[zMinWidth]="160"
|
|
148
|
+
(zOnItemClick)="_onDropdownItemClick($event)"
|
|
149
|
+
/>
|
|
150
|
+
}
|
|
151
|
+
</div>
|
|
152
|
+
`, isInline: true, styles: [":host{display:block}.z-table-actions :deep(button[z-button]){padding:.25rem;min-width:1.75rem;height:1.75rem}.z-table-actions :deep(button[z-button]):hover{background-color:var(--muted)}.z-table-actions :deep(button[z-button]).z-action-danger:hover{color:var(--destructive);background-color:color-mix(in srgb,var(--destructive) 10%,transparent)}\n"], dependencies: [{ 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: "component", type: ZIconComponent, selector: "z-icon, [z-icon]", inputs: ["class", "zType", "zSize", "zStrokeWidth", "zSvg"] }, { 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: "component", type: ZDropdownMenuComponent, selector: "z-dropdown-menu", inputs: ["zItems", "zLabel", "zIcon", "zButtonType", "zPosition", "zButtonSize", "zOffset", "zMinWidth", "zMaxWidth", "zDisabled"], outputs: ["zOnItemClick"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
153
|
+
}
|
|
154
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: ZTableActionsComponent, decorators: [{
|
|
155
|
+
type: Component,
|
|
156
|
+
args: [{ selector: 'z-table-actions', imports: [ZButtonComponent, ZIconComponent, ZTooltipDirective, ZDropdownMenuComponent], standalone: true, template: `
|
|
157
|
+
<div class="z-table-actions flex items-center justify-center gap-1">
|
|
158
|
+
@if (shouldShowAsButtons()) {
|
|
159
|
+
@for (action of allActions(); track action.key) {
|
|
160
|
+
@if (actionStates()[action.key]?.visible) {
|
|
161
|
+
<button
|
|
162
|
+
z-button
|
|
163
|
+
[zType]="action.type ?? 'outline'"
|
|
164
|
+
[zSize]="action.size ?? 'sm'"
|
|
165
|
+
[zDisabled]="actionStates()[action.key]?.disabled ?? false"
|
|
166
|
+
[class]="actionStates()[action.key]?.buttonClass ?? ''"
|
|
167
|
+
[attr.title]="action.tooltip"
|
|
168
|
+
z-tooltip
|
|
169
|
+
[zContent]="actionStates()[action.key]?.tooltip ?? ''"
|
|
170
|
+
(click)="_onActionClick(action, $event)"
|
|
171
|
+
>
|
|
172
|
+
@if (action.icon) {
|
|
173
|
+
<z-icon [zType]="action.icon" zSize="14" />
|
|
174
|
+
}
|
|
175
|
+
@if (action.label && !action.icon) {
|
|
176
|
+
{{ action.label }}
|
|
177
|
+
}
|
|
178
|
+
</button>
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
} @else {
|
|
182
|
+
<z-dropdown-menu
|
|
183
|
+
[zItems]="dropdownItems()"
|
|
184
|
+
zIcon="lucideMenu"
|
|
185
|
+
zButtonType="outline"
|
|
186
|
+
zPosition="bottom-right"
|
|
187
|
+
[zButtonSize]="zDropdownButtonSize()"
|
|
188
|
+
[zMinWidth]="160"
|
|
189
|
+
(zOnItemClick)="_onDropdownItemClick($event)"
|
|
190
|
+
/>
|
|
191
|
+
}
|
|
192
|
+
</div>
|
|
193
|
+
`, changeDetection: ChangeDetectionStrategy.OnPush, styles: [":host{display:block}.z-table-actions :deep(button[z-button]){padding:.25rem;min-width:1.75rem;height:1.75rem}.z-table-actions :deep(button[z-button]):hover{background-color:var(--muted)}.z-table-actions :deep(button[z-button]).z-action-danger:hover{color:var(--destructive);background-color:color-mix(in srgb,var(--destructive) 10%,transparent)}\n"] }]
|
|
194
|
+
}], propDecorators: { zConfig: [{ type: i0.Input, args: [{ isSignal: true, alias: "zConfig", required: true }] }], zRow: [{ type: i0.Input, args: [{ isSignal: true, alias: "zRow", required: true }] }], zRowId: [{ type: i0.Input, args: [{ isSignal: true, alias: "zRowId", required: true }] }], zDropdownButtonSize: [{ type: i0.Input, args: [{ isSignal: true, alias: "zDropdownButtonSize", required: false }] }], zActionClick: [{ type: i0.Output, args: ["zActionClick"] }] } });
|
|
195
|
+
|
|
196
|
+
class ZTableFilterComponent {
|
|
197
|
+
zColumn = input.required(...(ngDevMode ? [{ debugName: "zColumn" }] : []));
|
|
198
|
+
zTable = input.required(...(ngDevMode ? [{ debugName: "zTable" }] : []));
|
|
199
|
+
_translate = inject(TranslateService);
|
|
200
|
+
_currentLang = toSignal(this._translate.onLangChange.pipe(startWith({ lang: this._translate.currentLang })));
|
|
201
|
+
_debounceTimeout = null;
|
|
202
|
+
_debounceMs = 300;
|
|
203
|
+
_columnDef = computed(() => this.zColumn().columnDef, ...(ngDevMode ? [{ debugName: "_columnDef" }] : []));
|
|
204
|
+
filterType = computed(() => this._columnDef().filterType, ...(ngDevMode ? [{ debugName: "filterType" }] : []));
|
|
205
|
+
columnFilterValue = computed(() => this.zColumn().getFilterValue(), ...(ngDevMode ? [{ debugName: "columnFilterValue" }] : []));
|
|
206
|
+
minValue = computed(() => this.zColumn().getFacetedMinMaxValues()?.[0] ?? '', ...(ngDevMode ? [{ debugName: "minValue" }] : []));
|
|
207
|
+
maxValue = computed(() => this.zColumn().getFacetedMinMaxValues()?.[1] ?? '', ...(ngDevMode ? [{ debugName: "maxValue" }] : []));
|
|
208
|
+
rangeMinValue = computed(() => {
|
|
209
|
+
const value = this.columnFilterValue();
|
|
210
|
+
if (!Array.isArray(value)) {
|
|
211
|
+
return '';
|
|
72
212
|
}
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
213
|
+
return value[0] ?? '';
|
|
214
|
+
}, ...(ngDevMode ? [{ debugName: "rangeMinValue" }] : []));
|
|
215
|
+
rangeMaxValue = computed(() => {
|
|
216
|
+
const value = this.columnFilterValue();
|
|
217
|
+
if (!Array.isArray(value)) {
|
|
218
|
+
return '';
|
|
219
|
+
}
|
|
220
|
+
return value[1] ?? '';
|
|
221
|
+
}, ...(ngDevMode ? [{ debugName: "rangeMaxValue" }] : []));
|
|
222
|
+
minPlaceholder = computed(() => {
|
|
223
|
+
this._currentLang();
|
|
224
|
+
return this._translate.instant('i18n_z_ui_table_filter_min');
|
|
225
|
+
}, ...(ngDevMode ? [{ debugName: "minPlaceholder" }] : []));
|
|
226
|
+
maxPlaceholder = computed(() => {
|
|
227
|
+
this._currentLang();
|
|
228
|
+
return this._translate.instant('i18n_z_ui_table_filter_max');
|
|
229
|
+
}, ...(ngDevMode ? [{ debugName: "maxPlaceholder" }] : []));
|
|
230
|
+
searchPlaceholder = computed(() => {
|
|
231
|
+
this._currentLang();
|
|
232
|
+
return this._translate.instant('i18n_z_ui_table_filter_search');
|
|
233
|
+
}, ...(ngDevMode ? [{ debugName: "searchPlaceholder" }] : []));
|
|
234
|
+
allLabel = computed(() => {
|
|
235
|
+
this._currentLang();
|
|
236
|
+
return this._translate.instant('i18n_z_ui_table_filter_all');
|
|
237
|
+
}, ...(ngDevMode ? [{ debugName: "allLabel" }] : []));
|
|
238
|
+
datePlaceholder = computed(() => {
|
|
239
|
+
this._currentLang();
|
|
240
|
+
return this._translate.instant('i18n_z_ui_table_filter_date');
|
|
241
|
+
}, ...(ngDevMode ? [{ debugName: "datePlaceholder" }] : []));
|
|
242
|
+
dateRangePlaceholder = computed(() => {
|
|
243
|
+
this._currentLang();
|
|
244
|
+
return this._translate.instant('i18n_z_ui_table_filter_date_range');
|
|
245
|
+
}, ...(ngDevMode ? [{ debugName: "dateRangePlaceholder" }] : []));
|
|
246
|
+
dateValue = computed(() => {
|
|
247
|
+
const value = this.columnFilterValue();
|
|
248
|
+
if (value instanceof Date) {
|
|
249
|
+
return value;
|
|
250
|
+
}
|
|
251
|
+
return null;
|
|
252
|
+
}, ...(ngDevMode ? [{ debugName: "dateValue" }] : []));
|
|
253
|
+
dateRangeValue = computed(() => {
|
|
254
|
+
const value = this.columnFilterValue();
|
|
255
|
+
if (value && typeof value === 'object' && 'start' in value && 'end' in value) {
|
|
256
|
+
return value;
|
|
257
|
+
}
|
|
258
|
+
return null;
|
|
259
|
+
}, ...(ngDevMode ? [{ debugName: "dateRangeValue" }] : []));
|
|
260
|
+
selectOptions = computed(() => {
|
|
261
|
+
const columnDef = this._columnDef();
|
|
262
|
+
const allOption = { label: this.allLabel(), value: '' };
|
|
263
|
+
if (columnDef.filterOptions && columnDef.filterOptions.length > 0) {
|
|
264
|
+
return [allOption, ...columnDef.filterOptions];
|
|
265
|
+
}
|
|
266
|
+
const uniqueValues = Array.from(this.zColumn().getFacetedUniqueValues().keys()).sort().slice(0, 5000);
|
|
267
|
+
return [allOption, ...uniqueValues.map(v => ({ label: String(v), value: String(v) }))];
|
|
268
|
+
}, ...(ngDevMode ? [{ debugName: "selectOptions" }] : []));
|
|
269
|
+
ngOnDestroy() {
|
|
270
|
+
if (this._debounceTimeout) {
|
|
271
|
+
clearTimeout(this._debounceTimeout);
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
onMinChangeDebounced(value) {
|
|
275
|
+
this._debounce(() => {
|
|
276
|
+
const numValue = value === null || value === '' ? undefined : Number(value);
|
|
277
|
+
this.zColumn().setFilterValue((old) => [numValue, old?.[1]]);
|
|
79
278
|
});
|
|
80
279
|
}
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
280
|
+
onMaxChangeDebounced(value) {
|
|
281
|
+
this._debounce(() => {
|
|
282
|
+
const numValue = value === null || value === '' ? undefined : Number(value);
|
|
283
|
+
this.zColumn().setFilterValue((old) => [old?.[0], numValue]);
|
|
284
|
+
});
|
|
285
|
+
}
|
|
286
|
+
onTextChangeDebounced(value) {
|
|
287
|
+
this._debounce(() => {
|
|
288
|
+
this.zColumn().setFilterValue(value);
|
|
289
|
+
});
|
|
290
|
+
}
|
|
291
|
+
onSelectChange(value) {
|
|
292
|
+
if (typeof value === 'object' && value !== null && 'value' in value) {
|
|
293
|
+
this.zColumn().setFilterValue(value.value || undefined);
|
|
84
294
|
return;
|
|
85
295
|
}
|
|
86
|
-
this.
|
|
296
|
+
this.zColumn().setFilterValue(value || undefined);
|
|
87
297
|
}
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
298
|
+
onDateChange(value) {
|
|
299
|
+
this.zColumn().setFilterValue(value || undefined);
|
|
300
|
+
}
|
|
301
|
+
onDateRangeChange(value) {
|
|
302
|
+
if (value && value.start && value.end) {
|
|
303
|
+
this.zColumn().setFilterValue(value);
|
|
304
|
+
return;
|
|
95
305
|
}
|
|
96
|
-
|
|
306
|
+
this.zColumn().setFilterValue(undefined);
|
|
97
307
|
}
|
|
98
|
-
|
|
99
|
-
|
|
308
|
+
_debounce(fn) {
|
|
309
|
+
if (this._debounceTimeout) {
|
|
310
|
+
clearTimeout(this._debounceTimeout);
|
|
311
|
+
}
|
|
312
|
+
this._debounceTimeout = setTimeout(fn, this._debounceMs);
|
|
313
|
+
}
|
|
314
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: ZTableFilterComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
315
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.6", type: ZTableFilterComponent, isStandalone: true, selector: "z-table-filter", inputs: { zColumn: { classPropertyName: "zColumn", publicName: "zColumn", isSignal: true, isRequired: true, transformFunction: null }, zTable: { classPropertyName: "zTable", publicName: "zTable", isSignal: true, isRequired: true, transformFunction: null } }, host: { classAttribute: "z-table-filter block" }, ngImport: i0, template: `
|
|
316
|
+
@switch (filterType()) {
|
|
317
|
+
@case ('range') {
|
|
318
|
+
<div class="flex gap-1">
|
|
319
|
+
<z-input
|
|
320
|
+
zType="number"
|
|
321
|
+
zSize="sm"
|
|
322
|
+
[zPlaceholder]="minPlaceholder()"
|
|
323
|
+
[ngModel]="rangeMinValue()"
|
|
324
|
+
(ngModelChange)="onMinChangeDebounced($event)"
|
|
325
|
+
class="min-w-0 flex-1"
|
|
326
|
+
/>
|
|
327
|
+
<z-input
|
|
328
|
+
zType="number"
|
|
329
|
+
zSize="sm"
|
|
330
|
+
[zPlaceholder]="maxPlaceholder()"
|
|
331
|
+
[ngModel]="rangeMaxValue()"
|
|
332
|
+
(ngModelChange)="onMaxChangeDebounced($event)"
|
|
333
|
+
class="min-w-0 flex-1"
|
|
334
|
+
/>
|
|
335
|
+
</div>
|
|
336
|
+
}
|
|
337
|
+
@case ('date') {
|
|
338
|
+
<z-calendar
|
|
339
|
+
zMode="single"
|
|
340
|
+
zSize="sm"
|
|
341
|
+
zAllowClear
|
|
342
|
+
zValueType="date"
|
|
343
|
+
[zPlaceholder]="datePlaceholder()"
|
|
344
|
+
[ngModel]="dateValue()"
|
|
345
|
+
(ngModelChange)="onDateChange($event)"
|
|
346
|
+
class="w-full"
|
|
347
|
+
/>
|
|
348
|
+
}
|
|
349
|
+
@case ('date-range') {
|
|
350
|
+
<z-calendar
|
|
351
|
+
zMode="range"
|
|
352
|
+
zSize="sm"
|
|
353
|
+
zAllowClear
|
|
354
|
+
zValueType="date"
|
|
355
|
+
[zPlaceholder]="dateRangePlaceholder()"
|
|
356
|
+
[ngModel]="dateRangeValue()"
|
|
357
|
+
(ngModelChange)="onDateRangeChange($event)"
|
|
358
|
+
class="w-full"
|
|
359
|
+
/>
|
|
360
|
+
}
|
|
361
|
+
@case ('select') {
|
|
362
|
+
<z-select
|
|
363
|
+
zSize="sm"
|
|
364
|
+
[zOptions]="selectOptions()"
|
|
365
|
+
[ngModel]="columnFilterValue()?.toString() ?? ''"
|
|
366
|
+
[zPlaceholder]="allLabel()"
|
|
367
|
+
(ngModelChange)="onSelectChange($event)"
|
|
368
|
+
class="w-full min-w-0"
|
|
369
|
+
/>
|
|
370
|
+
}
|
|
371
|
+
@case ('number') {
|
|
372
|
+
<z-input
|
|
373
|
+
zType="number"
|
|
374
|
+
zSize="sm"
|
|
375
|
+
zAllowClear
|
|
376
|
+
[zPlaceholder]="searchPlaceholder()"
|
|
377
|
+
[ngModel]="columnFilterValue()?.toString() ?? ''"
|
|
378
|
+
(ngModelChange)="onTextChangeDebounced($event)"
|
|
379
|
+
class="w-full"
|
|
380
|
+
/>
|
|
381
|
+
}
|
|
382
|
+
@default {
|
|
383
|
+
<z-input
|
|
384
|
+
zType="text"
|
|
385
|
+
zSize="sm"
|
|
386
|
+
zAllowClear
|
|
387
|
+
[zPlaceholder]="searchPlaceholder()"
|
|
388
|
+
[ngModel]="(columnFilterValue() ?? '').toString()"
|
|
389
|
+
(ngModelChange)="onTextChangeDebounced($event)"
|
|
390
|
+
class="w-full"
|
|
391
|
+
/>
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
`, 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 });
|
|
100
395
|
}
|
|
101
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type:
|
|
102
|
-
type:
|
|
396
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: ZTableFilterComponent, decorators: [{
|
|
397
|
+
type: Component,
|
|
103
398
|
args: [{
|
|
104
|
-
selector: '
|
|
399
|
+
selector: 'z-table-filter',
|
|
400
|
+
imports: [FormsModule, ZInputComponent, ZSelectComponent, ZCalendarComponent],
|
|
105
401
|
standalone: true,
|
|
402
|
+
template: `
|
|
403
|
+
@switch (filterType()) {
|
|
404
|
+
@case ('range') {
|
|
405
|
+
<div class="flex gap-1">
|
|
406
|
+
<z-input
|
|
407
|
+
zType="number"
|
|
408
|
+
zSize="sm"
|
|
409
|
+
[zPlaceholder]="minPlaceholder()"
|
|
410
|
+
[ngModel]="rangeMinValue()"
|
|
411
|
+
(ngModelChange)="onMinChangeDebounced($event)"
|
|
412
|
+
class="min-w-0 flex-1"
|
|
413
|
+
/>
|
|
414
|
+
<z-input
|
|
415
|
+
zType="number"
|
|
416
|
+
zSize="sm"
|
|
417
|
+
[zPlaceholder]="maxPlaceholder()"
|
|
418
|
+
[ngModel]="rangeMaxValue()"
|
|
419
|
+
(ngModelChange)="onMaxChangeDebounced($event)"
|
|
420
|
+
class="min-w-0 flex-1"
|
|
421
|
+
/>
|
|
422
|
+
</div>
|
|
423
|
+
}
|
|
424
|
+
@case ('date') {
|
|
425
|
+
<z-calendar
|
|
426
|
+
zMode="single"
|
|
427
|
+
zSize="sm"
|
|
428
|
+
zAllowClear
|
|
429
|
+
zValueType="date"
|
|
430
|
+
[zPlaceholder]="datePlaceholder()"
|
|
431
|
+
[ngModel]="dateValue()"
|
|
432
|
+
(ngModelChange)="onDateChange($event)"
|
|
433
|
+
class="w-full"
|
|
434
|
+
/>
|
|
435
|
+
}
|
|
436
|
+
@case ('date-range') {
|
|
437
|
+
<z-calendar
|
|
438
|
+
zMode="range"
|
|
439
|
+
zSize="sm"
|
|
440
|
+
zAllowClear
|
|
441
|
+
zValueType="date"
|
|
442
|
+
[zPlaceholder]="dateRangePlaceholder()"
|
|
443
|
+
[ngModel]="dateRangeValue()"
|
|
444
|
+
(ngModelChange)="onDateRangeChange($event)"
|
|
445
|
+
class="w-full"
|
|
446
|
+
/>
|
|
447
|
+
}
|
|
448
|
+
@case ('select') {
|
|
449
|
+
<z-select
|
|
450
|
+
zSize="sm"
|
|
451
|
+
[zOptions]="selectOptions()"
|
|
452
|
+
[ngModel]="columnFilterValue()?.toString() ?? ''"
|
|
453
|
+
[zPlaceholder]="allLabel()"
|
|
454
|
+
(ngModelChange)="onSelectChange($event)"
|
|
455
|
+
class="w-full min-w-0"
|
|
456
|
+
/>
|
|
457
|
+
}
|
|
458
|
+
@case ('number') {
|
|
459
|
+
<z-input
|
|
460
|
+
zType="number"
|
|
461
|
+
zSize="sm"
|
|
462
|
+
zAllowClear
|
|
463
|
+
[zPlaceholder]="searchPlaceholder()"
|
|
464
|
+
[ngModel]="columnFilterValue()?.toString() ?? ''"
|
|
465
|
+
(ngModelChange)="onTextChangeDebounced($event)"
|
|
466
|
+
class="w-full"
|
|
467
|
+
/>
|
|
468
|
+
}
|
|
469
|
+
@default {
|
|
470
|
+
<z-input
|
|
471
|
+
zType="text"
|
|
472
|
+
zSize="sm"
|
|
473
|
+
zAllowClear
|
|
474
|
+
[zPlaceholder]="searchPlaceholder()"
|
|
475
|
+
[ngModel]="(columnFilterValue() ?? '').toString()"
|
|
476
|
+
(ngModelChange)="onTextChangeDebounced($event)"
|
|
477
|
+
class="w-full"
|
|
478
|
+
/>
|
|
479
|
+
}
|
|
480
|
+
}
|
|
481
|
+
`,
|
|
482
|
+
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
106
483
|
host: {
|
|
107
|
-
|
|
108
|
-
'(mouseleave)': 'onMouseLeave()',
|
|
484
|
+
class: 'z-table-filter block',
|
|
109
485
|
},
|
|
110
486
|
}]
|
|
111
|
-
}], propDecorators: {
|
|
487
|
+
}], propDecorators: { zColumn: [{ type: i0.Input, args: [{ isSignal: true, alias: "zColumn", required: true }] }], zTable: [{ type: i0.Input, args: [{ isSignal: true, alias: "zTable", required: true }] }] } });
|
|
112
488
|
|
|
113
489
|
const Z_DEFAULT_ROW_HEIGHT = 40;
|
|
114
490
|
const Z_DEFAULT_VIRTUAL_OVERSCAN = 5;
|
|
@@ -134,7 +510,7 @@ const isFooterConfig = (config) => {
|
|
|
134
510
|
}
|
|
135
511
|
return config.constructor === Object;
|
|
136
512
|
};
|
|
137
|
-
const
|
|
513
|
+
const getHeaderOrFooterConfigInternal = (col, type) => {
|
|
138
514
|
const empty = {
|
|
139
515
|
content: undefined,
|
|
140
516
|
class: undefined,
|
|
@@ -143,23 +519,31 @@ const getHeaderConfig = (col) => {
|
|
|
143
519
|
tooltip: undefined,
|
|
144
520
|
rowSpan: undefined,
|
|
145
521
|
colSpan: undefined,
|
|
522
|
+
contentClass: undefined,
|
|
523
|
+
contentStyle: undefined,
|
|
146
524
|
};
|
|
147
525
|
if (!col) {
|
|
148
526
|
return empty;
|
|
149
527
|
}
|
|
150
|
-
|
|
151
|
-
|
|
528
|
+
const config = type === 'header' ? col.header : col.footer;
|
|
529
|
+
const isConfigFn = type === 'header' ? isHeaderConfig : isFooterConfig;
|
|
530
|
+
if (!isConfigFn(config)) {
|
|
531
|
+
return { ...empty, content: config };
|
|
152
532
|
}
|
|
533
|
+
const typedConfig = config;
|
|
153
534
|
return {
|
|
154
|
-
content:
|
|
155
|
-
class:
|
|
156
|
-
style:
|
|
157
|
-
align:
|
|
158
|
-
tooltip:
|
|
159
|
-
rowSpan:
|
|
160
|
-
colSpan:
|
|
535
|
+
content: typedConfig.content,
|
|
536
|
+
class: typedConfig.class,
|
|
537
|
+
style: typedConfig.style,
|
|
538
|
+
align: typedConfig.align,
|
|
539
|
+
tooltip: typedConfig.tooltip,
|
|
540
|
+
rowSpan: typedConfig.rowSpan,
|
|
541
|
+
colSpan: typedConfig.colSpan,
|
|
542
|
+
contentClass: typedConfig.contentClass,
|
|
543
|
+
contentStyle: typedConfig.contentStyle,
|
|
161
544
|
};
|
|
162
545
|
};
|
|
546
|
+
const getHeaderConfig = (col) => getHeaderOrFooterConfigInternal(col, 'header');
|
|
163
547
|
const getBodyConfig = (col, ctx) => {
|
|
164
548
|
const empty = {
|
|
165
549
|
content: undefined,
|
|
@@ -198,32 +582,7 @@ const getBodyConfig = (col, ctx) => {
|
|
|
198
582
|
tooltip,
|
|
199
583
|
};
|
|
200
584
|
};
|
|
201
|
-
const getFooterConfig = (col) =>
|
|
202
|
-
const empty = {
|
|
203
|
-
content: undefined,
|
|
204
|
-
class: undefined,
|
|
205
|
-
style: undefined,
|
|
206
|
-
align: undefined,
|
|
207
|
-
tooltip: undefined,
|
|
208
|
-
rowSpan: undefined,
|
|
209
|
-
colSpan: undefined,
|
|
210
|
-
};
|
|
211
|
-
if (!col) {
|
|
212
|
-
return empty;
|
|
213
|
-
}
|
|
214
|
-
if (!isFooterConfig(col.footer)) {
|
|
215
|
-
return { ...empty, content: col.footer };
|
|
216
|
-
}
|
|
217
|
-
return {
|
|
218
|
-
content: col.footer.content,
|
|
219
|
-
class: col.footer.class,
|
|
220
|
-
style: col.footer.style,
|
|
221
|
-
align: col.footer.align,
|
|
222
|
-
tooltip: col.footer.tooltip,
|
|
223
|
-
rowSpan: col.footer.rowSpan,
|
|
224
|
-
colSpan: col.footer.colSpan,
|
|
225
|
-
};
|
|
226
|
-
};
|
|
585
|
+
const getFooterConfig = (col) => getHeaderOrFooterConfigInternal(col, 'footer');
|
|
227
586
|
const getHeaderContent = (col) => getHeaderConfig(col).content;
|
|
228
587
|
const getBodyContent = (col) => {
|
|
229
588
|
if (!col?.body) {
|
|
@@ -542,6 +901,204 @@ function columnConfigToColumnDef(config) {
|
|
|
542
901
|
return columnDef;
|
|
543
902
|
}
|
|
544
903
|
|
|
904
|
+
class ZTableIconTextComponent {
|
|
905
|
+
zText = input('', ...(ngDevMode ? [{ debugName: "zText" }] : []));
|
|
906
|
+
zTooltip = input('', ...(ngDevMode ? [{ debugName: "zTooltip" }] : []));
|
|
907
|
+
zTriggerElement = input(null, ...(ngDevMode ? [{ debugName: "zTriggerElement" }] : []));
|
|
908
|
+
_hostEl = inject(ElementRef).nativeElement;
|
|
909
|
+
triggerEl = computed(() => this.zTriggerElement() ?? this._hostEl, ...(ngDevMode ? [{ debugName: "triggerEl" }] : []));
|
|
910
|
+
parts = computed(() => parseIconString(this.zText()), ...(ngDevMode ? [{ debugName: "parts" }] : []));
|
|
911
|
+
tooltipContent = computed(() => {
|
|
912
|
+
const tooltip = this.zTooltip();
|
|
913
|
+
if (tooltip) {
|
|
914
|
+
if (typeof tooltip === 'string') {
|
|
915
|
+
return stripIconSyntax(tooltip);
|
|
916
|
+
}
|
|
917
|
+
if (tooltip.content && typeof tooltip.content === 'string') {
|
|
918
|
+
return stripIconSyntax(tooltip.content);
|
|
919
|
+
}
|
|
920
|
+
}
|
|
921
|
+
return stripIconSyntax(this.zText());
|
|
922
|
+
}, ...(ngDevMode ? [{ debugName: "tooltipContent" }] : []));
|
|
923
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: ZTableIconTextComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
924
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.6", type: ZTableIconTextComponent, isStandalone: true, selector: "z-table-icon-text", inputs: { zText: { classPropertyName: "zText", publicName: "zText", isSignal: true, isRequired: false, transformFunction: null }, zTooltip: { classPropertyName: "zTooltip", publicName: "zTooltip", isSignal: true, isRequired: false, transformFunction: null }, zTriggerElement: { classPropertyName: "zTriggerElement", publicName: "zTriggerElement", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: `
|
|
925
|
+
<span class="z-table-icon-text-inner">
|
|
926
|
+
@for (part of parts(); track $index) {
|
|
927
|
+
@if (part.type === 'icon') {
|
|
928
|
+
<z-icon
|
|
929
|
+
class="shrink-0"
|
|
930
|
+
[zType]="$any(part.value)"
|
|
931
|
+
[zSize]="$any(part.size ?? 14)"
|
|
932
|
+
[zStrokeWidth]="part.strokeWidth ?? 1.5"
|
|
933
|
+
[ngClass]="part.class ?? ''"
|
|
934
|
+
/>
|
|
935
|
+
} @else {
|
|
936
|
+
<span
|
|
937
|
+
class="truncate select-text"
|
|
938
|
+
z-tooltip
|
|
939
|
+
[zContent]="tooltipContent()"
|
|
940
|
+
[zTriggerElement]="triggerEl()"
|
|
941
|
+
[innerHTML]="part.value | zSafeHtml"
|
|
942
|
+
></span>
|
|
943
|
+
}
|
|
944
|
+
}
|
|
945
|
+
</span>
|
|
946
|
+
`, isInline: true, styles: [":host{display:inline-flex;align-items:center;min-width:0;max-width:100%}.z-table-icon-text-inner{display:inline-flex;align-items:center;gap:4px;line-height:1.2;min-width:0;max-width:100%}span.truncate{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}\n"], dependencies: [{ kind: "directive", type: NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "component", type: ZIconComponent, selector: "z-icon, [z-icon]", inputs: ["class", "zType", "zSize", "zStrokeWidth", "zSvg"] }, { 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: "pipe", type: ZSafeHtmlPipe, name: "zSafeHtml" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
947
|
+
}
|
|
948
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: ZTableIconTextComponent, decorators: [{
|
|
949
|
+
type: Component,
|
|
950
|
+
args: [{ selector: 'z-table-icon-text', imports: [NgClass, ZIconComponent, ZTooltipDirective, ZSafeHtmlPipe], standalone: true, template: `
|
|
951
|
+
<span class="z-table-icon-text-inner">
|
|
952
|
+
@for (part of parts(); track $index) {
|
|
953
|
+
@if (part.type === 'icon') {
|
|
954
|
+
<z-icon
|
|
955
|
+
class="shrink-0"
|
|
956
|
+
[zType]="$any(part.value)"
|
|
957
|
+
[zSize]="$any(part.size ?? 14)"
|
|
958
|
+
[zStrokeWidth]="part.strokeWidth ?? 1.5"
|
|
959
|
+
[ngClass]="part.class ?? ''"
|
|
960
|
+
/>
|
|
961
|
+
} @else {
|
|
962
|
+
<span
|
|
963
|
+
class="truncate select-text"
|
|
964
|
+
z-tooltip
|
|
965
|
+
[zContent]="tooltipContent()"
|
|
966
|
+
[zTriggerElement]="triggerEl()"
|
|
967
|
+
[innerHTML]="part.value | zSafeHtml"
|
|
968
|
+
></span>
|
|
969
|
+
}
|
|
970
|
+
}
|
|
971
|
+
</span>
|
|
972
|
+
`, changeDetection: ChangeDetectionStrategy.OnPush, styles: [":host{display:inline-flex;align-items:center;min-width:0;max-width:100%}.z-table-icon-text-inner{display:inline-flex;align-items:center;gap:4px;line-height:1.2;min-width:0;max-width:100%}span.truncate{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}\n"] }]
|
|
973
|
+
}], propDecorators: { zText: [{ type: i0.Input, args: [{ isSignal: true, alias: "zText", required: false }] }], zTooltip: [{ type: i0.Input, args: [{ isSignal: true, alias: "zTooltip", required: false }] }], zTriggerElement: [{ type: i0.Input, args: [{ isSignal: true, alias: "zTriggerElement", required: false }] }] } });
|
|
974
|
+
|
|
975
|
+
class ZTableRowHoverDirective {
|
|
976
|
+
zTableRowHover = input(null, ...(ngDevMode ? [{ debugName: "zTableRowHover" }] : []));
|
|
977
|
+
_elementRef = inject((ElementRef));
|
|
978
|
+
_renderer = inject(Renderer2);
|
|
979
|
+
onMouseEnter() {
|
|
980
|
+
this._handleHover(true);
|
|
981
|
+
}
|
|
982
|
+
onMouseLeave() {
|
|
983
|
+
this._handleHover(false);
|
|
984
|
+
}
|
|
985
|
+
_handleHover(isHover) {
|
|
986
|
+
const row = this._elementRef.nativeElement;
|
|
987
|
+
const table = this.zTableRowHover() || this._findParentTable(row);
|
|
988
|
+
if (!table) {
|
|
989
|
+
return;
|
|
990
|
+
}
|
|
991
|
+
const cellsInCurrentRow = Array.from(row.children);
|
|
992
|
+
cellsInCurrentRow.forEach(cell => {
|
|
993
|
+
this._toggleCellHover(cell, isHover);
|
|
994
|
+
});
|
|
995
|
+
const tbody = table.querySelector('tbody');
|
|
996
|
+
if (!tbody) {
|
|
997
|
+
return;
|
|
998
|
+
}
|
|
999
|
+
const allRows = Array.from(tbody.querySelectorAll('tr'));
|
|
1000
|
+
const currentRowIndex = allRows.indexOf(row);
|
|
1001
|
+
if (currentRowIndex === -1) {
|
|
1002
|
+
return;
|
|
1003
|
+
}
|
|
1004
|
+
const grid = [];
|
|
1005
|
+
for (let rowIdx = 0; rowIdx < allRows.length; rowIdx++) {
|
|
1006
|
+
const r = allRows[rowIdx];
|
|
1007
|
+
const cellsInRow = Array.from(r.children);
|
|
1008
|
+
cellsInRow.forEach(cell => {
|
|
1009
|
+
const rowSpan = parseInt(cell.getAttribute('rowspan') || '1', 10);
|
|
1010
|
+
const rowEnd = rowIdx + rowSpan - 1;
|
|
1011
|
+
grid.push({
|
|
1012
|
+
cell,
|
|
1013
|
+
rowStart: rowIdx,
|
|
1014
|
+
rowEnd: rowEnd,
|
|
1015
|
+
});
|
|
1016
|
+
});
|
|
1017
|
+
}
|
|
1018
|
+
grid.forEach(item => {
|
|
1019
|
+
const coverCurrentRow = item.rowStart < currentRowIndex && item.rowEnd >= currentRowIndex;
|
|
1020
|
+
if (!coverCurrentRow) {
|
|
1021
|
+
return;
|
|
1022
|
+
}
|
|
1023
|
+
this._toggleCellHover(item.cell, isHover);
|
|
1024
|
+
});
|
|
1025
|
+
}
|
|
1026
|
+
_toggleCellHover(cell, isHover) {
|
|
1027
|
+
if (isHover) {
|
|
1028
|
+
this._renderer.addClass(cell, 'z-row-hover');
|
|
1029
|
+
return;
|
|
1030
|
+
}
|
|
1031
|
+
this._renderer.removeClass(cell, 'z-row-hover');
|
|
1032
|
+
}
|
|
1033
|
+
_findParentTable(element) {
|
|
1034
|
+
let current = element.parentElement;
|
|
1035
|
+
while (current) {
|
|
1036
|
+
if (current.tagName === 'TABLE') {
|
|
1037
|
+
return current;
|
|
1038
|
+
}
|
|
1039
|
+
current = current.parentElement;
|
|
1040
|
+
}
|
|
1041
|
+
return null;
|
|
1042
|
+
}
|
|
1043
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: ZTableRowHoverDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive });
|
|
1044
|
+
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "21.0.6", type: ZTableRowHoverDirective, isStandalone: true, selector: "[z-table-row-hover], [zTableRowHover]", inputs: { zTableRowHover: { classPropertyName: "zTableRowHover", publicName: "zTableRowHover", isSignal: true, isRequired: false, transformFunction: null } }, host: { listeners: { "mouseenter": "onMouseEnter()", "mouseleave": "onMouseLeave()" } }, ngImport: i0 });
|
|
1045
|
+
}
|
|
1046
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: ZTableRowHoverDirective, decorators: [{
|
|
1047
|
+
type: Directive,
|
|
1048
|
+
args: [{
|
|
1049
|
+
selector: '[z-table-row-hover], [zTableRowHover]',
|
|
1050
|
+
standalone: true,
|
|
1051
|
+
host: {
|
|
1052
|
+
'(mouseenter)': 'onMouseEnter()',
|
|
1053
|
+
'(mouseleave)': 'onMouseLeave()',
|
|
1054
|
+
},
|
|
1055
|
+
}]
|
|
1056
|
+
}], propDecorators: { zTableRowHover: [{ type: i0.Input, args: [{ isSignal: true, alias: "zTableRowHover", required: false }] }] } });
|
|
1057
|
+
|
|
1058
|
+
class ZTableResizerDirective {
|
|
1059
|
+
zTableResizer = input.required(...(ngDevMode ? [{ debugName: "zTableResizer" }] : []));
|
|
1060
|
+
_elementRef = inject((ElementRef));
|
|
1061
|
+
_ngZone = inject(NgZone);
|
|
1062
|
+
_mousedownHandler = null;
|
|
1063
|
+
_touchstartHandler = null;
|
|
1064
|
+
ngAfterViewInit() {
|
|
1065
|
+
const element = this._elementRef.nativeElement;
|
|
1066
|
+
const header = this.zTableResizer();
|
|
1067
|
+
this._mousedownHandler = (event) => {
|
|
1068
|
+
const resizeHandler = header.getResizeHandler();
|
|
1069
|
+
resizeHandler(event);
|
|
1070
|
+
};
|
|
1071
|
+
this._touchstartHandler = (event) => {
|
|
1072
|
+
const resizeHandler = header.getResizeHandler();
|
|
1073
|
+
resizeHandler(event);
|
|
1074
|
+
};
|
|
1075
|
+
this._ngZone.runOutsideAngular(() => {
|
|
1076
|
+
element.addEventListener('mousedown', this._mousedownHandler);
|
|
1077
|
+
element.addEventListener('touchstart', this._touchstartHandler, { passive: false });
|
|
1078
|
+
});
|
|
1079
|
+
}
|
|
1080
|
+
ngOnDestroy() {
|
|
1081
|
+
const element = this._elementRef.nativeElement;
|
|
1082
|
+
if (this._mousedownHandler) {
|
|
1083
|
+
element.removeEventListener('mousedown', this._mousedownHandler);
|
|
1084
|
+
this._mousedownHandler = null;
|
|
1085
|
+
}
|
|
1086
|
+
if (this._touchstartHandler) {
|
|
1087
|
+
element.removeEventListener('touchstart', this._touchstartHandler);
|
|
1088
|
+
this._touchstartHandler = null;
|
|
1089
|
+
}
|
|
1090
|
+
}
|
|
1091
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: ZTableResizerDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive });
|
|
1092
|
+
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "21.0.6", type: ZTableResizerDirective, isStandalone: true, selector: "[z-table-resizer],[zTableResizer]", inputs: { zTableResizer: { classPropertyName: "zTableResizer", publicName: "zTableResizer", isSignal: true, isRequired: true, transformFunction: null } }, ngImport: i0 });
|
|
1093
|
+
}
|
|
1094
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: ZTableResizerDirective, decorators: [{
|
|
1095
|
+
type: Directive,
|
|
1096
|
+
args: [{
|
|
1097
|
+
selector: '[z-table-resizer],[zTableResizer]',
|
|
1098
|
+
standalone: true,
|
|
1099
|
+
}]
|
|
1100
|
+
}], propDecorators: { zTableResizer: [{ type: i0.Input, args: [{ isSignal: true, alias: "zTableResizer", required: true }] }] } });
|
|
1101
|
+
|
|
545
1102
|
class ZTableCellBottomPipe {
|
|
546
1103
|
transform(cell, columns, allRows) {
|
|
547
1104
|
if (allRows.length === 0) {
|
|
@@ -700,7 +1257,23 @@ class ZTableColumnConfigPipe {
|
|
|
700
1257
|
}
|
|
701
1258
|
case 'headerAlignClass': {
|
|
702
1259
|
const headerConfig = getHeaderConfig(columnConfig);
|
|
703
|
-
return headerConfig.align ? Z_TABLE_ALIGN_MAP[headerConfig.align] || '' : '';
|
|
1260
|
+
return headerConfig.align ? Z_TABLE_ALIGN_MAP[headerConfig.align] || '' : '';
|
|
1261
|
+
}
|
|
1262
|
+
case 'headerContentClass': {
|
|
1263
|
+
const headerConfig = getHeaderConfig(columnConfig);
|
|
1264
|
+
const { contentClass } = headerConfig;
|
|
1265
|
+
if (typeof contentClass === 'function') {
|
|
1266
|
+
return '';
|
|
1267
|
+
}
|
|
1268
|
+
return contentClass || '';
|
|
1269
|
+
}
|
|
1270
|
+
case 'headerContentStyle': {
|
|
1271
|
+
const headerConfig = getHeaderConfig(columnConfig);
|
|
1272
|
+
const { contentStyle } = headerConfig;
|
|
1273
|
+
if (typeof contentStyle === 'function') {
|
|
1274
|
+
return {};
|
|
1275
|
+
}
|
|
1276
|
+
return contentStyle || {};
|
|
704
1277
|
}
|
|
705
1278
|
case 'footerClass': {
|
|
706
1279
|
const footerConfig = getFooterConfig(columnConfig);
|
|
@@ -718,6 +1291,22 @@ class ZTableColumnConfigPipe {
|
|
|
718
1291
|
const footerConfig = getFooterConfig(columnConfig);
|
|
719
1292
|
return footerConfig.align ? Z_TABLE_ALIGN_MAP[footerConfig.align] || '' : '';
|
|
720
1293
|
}
|
|
1294
|
+
case 'footerContentClass': {
|
|
1295
|
+
const footerConfig = getFooterConfig(columnConfig);
|
|
1296
|
+
const { contentClass } = footerConfig;
|
|
1297
|
+
if (typeof contentClass === 'function') {
|
|
1298
|
+
return '';
|
|
1299
|
+
}
|
|
1300
|
+
return contentClass || '';
|
|
1301
|
+
}
|
|
1302
|
+
case 'footerContentStyle': {
|
|
1303
|
+
const footerConfig = getFooterConfig(columnConfig);
|
|
1304
|
+
const { contentStyle } = footerConfig;
|
|
1305
|
+
if (typeof contentStyle === 'function') {
|
|
1306
|
+
return {};
|
|
1307
|
+
}
|
|
1308
|
+
return contentStyle || {};
|
|
1309
|
+
}
|
|
721
1310
|
case 'hasBodyRowSpan':
|
|
722
1311
|
return columnConfig ? isBodyConfig(columnConfig.body) && columnConfig.body.rowSpan !== undefined : false;
|
|
723
1312
|
case 'enableOrdering':
|
|
@@ -1197,398 +1786,115 @@ class ZTableRowPipe {
|
|
|
1197
1786
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: ZTableRowPipe, deps: [], target: i0.ɵɵFactoryTarget.Pipe });
|
|
1198
1787
|
static ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "21.0.6", ngImport: i0, type: ZTableRowPipe, isStandalone: true, name: "zTableRow" });
|
|
1199
1788
|
}
|
|
1200
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: ZTableRowPipe, decorators: [{
|
|
1201
|
-
type: Pipe,
|
|
1202
|
-
args: [{
|
|
1203
|
-
name: 'zTableRow',
|
|
1204
|
-
standalone: true,
|
|
1205
|
-
pure: true,
|
|
1206
|
-
}]
|
|
1207
|
-
}] });
|
|
1208
|
-
|
|
1209
|
-
class ZTableSpanPipe {
|
|
1210
|
-
transform(item, columns, property, allRows) {
|
|
1211
|
-
const columnConfig = findColumnConfig(item.column.id, columns);
|
|
1212
|
-
switch (property) {
|
|
1213
|
-
case 'cellRowSpan':
|
|
1214
|
-
return calculateCellRowSpan(item, columnConfig, allRows ?? []);
|
|
1215
|
-
case 'cellColSpan':
|
|
1216
|
-
return calculateCellColSpan(item, columnConfig);
|
|
1217
|
-
case 'headerRowSpan':
|
|
1218
|
-
return calculateHeaderRowSpan(item, columnConfig, columns);
|
|
1219
|
-
case 'headerColSpan':
|
|
1220
|
-
return calculateHeaderColSpan(item, columnConfig);
|
|
1221
|
-
case 'footerRowSpan':
|
|
1222
|
-
return calculateFooterRowSpan(item, columnConfig);
|
|
1223
|
-
case 'footerColSpan':
|
|
1224
|
-
return calculateFooterColSpan(item, columnConfig);
|
|
1225
|
-
case 'shouldRender':
|
|
1226
|
-
return shouldCellRenderUtil(item, columnConfig, allRows ?? []);
|
|
1227
|
-
default:
|
|
1228
|
-
return null;
|
|
1229
|
-
}
|
|
1230
|
-
}
|
|
1231
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: ZTableSpanPipe, deps: [], target: i0.ɵɵFactoryTarget.Pipe });
|
|
1232
|
-
static ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "21.0.6", ngImport: i0, type: ZTableSpanPipe, isStandalone: true, name: "zTableSpan" });
|
|
1233
|
-
}
|
|
1234
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: ZTableSpanPipe, decorators: [{
|
|
1235
|
-
type: Pipe,
|
|
1236
|
-
args: [{
|
|
1237
|
-
name: 'zTableSpan',
|
|
1238
|
-
standalone: true,
|
|
1239
|
-
pure: true,
|
|
1240
|
-
}]
|
|
1241
|
-
}] });
|
|
1242
|
-
|
|
1243
|
-
class ZTableIsTemplateRefPipe {
|
|
1244
|
-
transform(value) {
|
|
1245
|
-
return value instanceof TemplateRef;
|
|
1246
|
-
}
|
|
1247
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: ZTableIsTemplateRefPipe, deps: [], target: i0.ɵɵFactoryTarget.Pipe });
|
|
1248
|
-
static ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "21.0.6", ngImport: i0, type: ZTableIsTemplateRefPipe, isStandalone: true, name: "zTableIsTemplateRef" });
|
|
1249
|
-
}
|
|
1250
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: ZTableIsTemplateRefPipe, decorators: [{
|
|
1251
|
-
type: Pipe,
|
|
1252
|
-
args: [{
|
|
1253
|
-
name: 'zTableIsTemplateRef',
|
|
1254
|
-
standalone: true,
|
|
1255
|
-
pure: true,
|
|
1256
|
-
}]
|
|
1257
|
-
}] });
|
|
1258
|
-
|
|
1259
|
-
class ZTableBodyCellRenderPipe {
|
|
1260
|
-
transform(cell, allCells, columns) {
|
|
1261
|
-
return shouldBodyCellRenderColSpan(cell, allCells, columns);
|
|
1262
|
-
}
|
|
1263
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: ZTableBodyCellRenderPipe, deps: [], target: i0.ɵɵFactoryTarget.Pipe });
|
|
1264
|
-
static ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "21.0.6", ngImport: i0, type: ZTableBodyCellRenderPipe, isStandalone: true, name: "zTableBodyCellRender" });
|
|
1265
|
-
}
|
|
1266
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: ZTableBodyCellRenderPipe, decorators: [{
|
|
1267
|
-
type: Pipe,
|
|
1268
|
-
args: [{
|
|
1269
|
-
name: 'zTableBodyCellRender',
|
|
1270
|
-
standalone: true,
|
|
1271
|
-
pure: true,
|
|
1272
|
-
}]
|
|
1273
|
-
}] });
|
|
1274
|
-
|
|
1275
|
-
class ZTableFooterRenderPipe {
|
|
1276
|
-
transform(header, allHeaders, columns) {
|
|
1277
|
-
return shouldFooterRender(header, allHeaders, columns);
|
|
1278
|
-
}
|
|
1279
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: ZTableFooterRenderPipe, deps: [], target: i0.ɵɵFactoryTarget.Pipe });
|
|
1280
|
-
static ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "21.0.6", ngImport: i0, type: ZTableFooterRenderPipe, isStandalone: true, name: "zTableFooterRender" });
|
|
1281
|
-
}
|
|
1282
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: ZTableFooterRenderPipe, decorators: [{
|
|
1283
|
-
type: Pipe,
|
|
1284
|
-
args: [{
|
|
1285
|
-
name: 'zTableFooterRender',
|
|
1286
|
-
standalone: true,
|
|
1287
|
-
pure: true,
|
|
1288
|
-
}]
|
|
1289
|
-
}] });
|
|
1290
|
-
|
|
1291
|
-
class ZTableHeaderRenderPipe {
|
|
1292
|
-
transform(header, allHeaders, columns) {
|
|
1293
|
-
return shouldHeaderRender(header, allHeaders, columns);
|
|
1294
|
-
}
|
|
1295
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: ZTableHeaderRenderPipe, deps: [], target: i0.ɵɵFactoryTarget.Pipe });
|
|
1296
|
-
static ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "21.0.6", ngImport: i0, type: ZTableHeaderRenderPipe, isStandalone: true, name: "zTableHeaderRender" });
|
|
1297
|
-
}
|
|
1298
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: ZTableHeaderRenderPipe, decorators: [{
|
|
1299
|
-
type: Pipe,
|
|
1300
|
-
args: [{
|
|
1301
|
-
name: 'zTableHeaderRender',
|
|
1302
|
-
standalone: true,
|
|
1303
|
-
pure: true,
|
|
1304
|
-
}]
|
|
1305
|
-
}] });
|
|
1306
|
-
|
|
1307
|
-
class ZTableFilterComponent {
|
|
1308
|
-
zColumn = input.required(...(ngDevMode ? [{ debugName: "zColumn" }] : []));
|
|
1309
|
-
zTable = input.required(...(ngDevMode ? [{ debugName: "zTable" }] : []));
|
|
1310
|
-
_translate = inject(TranslateService);
|
|
1311
|
-
_currentLang = toSignal(this._translate.onLangChange.pipe(startWith({ lang: this._translate.currentLang })));
|
|
1312
|
-
_debounceTimeout = null;
|
|
1313
|
-
_debounceMs = 300;
|
|
1314
|
-
_columnDef = computed(() => this.zColumn().columnDef, ...(ngDevMode ? [{ debugName: "_columnDef" }] : []));
|
|
1315
|
-
filterType = computed(() => this._columnDef().filterType, ...(ngDevMode ? [{ debugName: "filterType" }] : []));
|
|
1316
|
-
columnFilterValue = computed(() => this.zColumn().getFilterValue(), ...(ngDevMode ? [{ debugName: "columnFilterValue" }] : []));
|
|
1317
|
-
minValue = computed(() => this.zColumn().getFacetedMinMaxValues()?.[0] ?? '', ...(ngDevMode ? [{ debugName: "minValue" }] : []));
|
|
1318
|
-
maxValue = computed(() => this.zColumn().getFacetedMinMaxValues()?.[1] ?? '', ...(ngDevMode ? [{ debugName: "maxValue" }] : []));
|
|
1319
|
-
rangeMinValue = computed(() => {
|
|
1320
|
-
const value = this.columnFilterValue();
|
|
1321
|
-
if (!Array.isArray(value)) {
|
|
1322
|
-
return '';
|
|
1323
|
-
}
|
|
1324
|
-
return value[0] ?? '';
|
|
1325
|
-
}, ...(ngDevMode ? [{ debugName: "rangeMinValue" }] : []));
|
|
1326
|
-
rangeMaxValue = computed(() => {
|
|
1327
|
-
const value = this.columnFilterValue();
|
|
1328
|
-
if (!Array.isArray(value)) {
|
|
1329
|
-
return '';
|
|
1330
|
-
}
|
|
1331
|
-
return value[1] ?? '';
|
|
1332
|
-
}, ...(ngDevMode ? [{ debugName: "rangeMaxValue" }] : []));
|
|
1333
|
-
minPlaceholder = computed(() => {
|
|
1334
|
-
this._currentLang();
|
|
1335
|
-
return this._translate.instant('i18n_z_ui_table_filter_min');
|
|
1336
|
-
}, ...(ngDevMode ? [{ debugName: "minPlaceholder" }] : []));
|
|
1337
|
-
maxPlaceholder = computed(() => {
|
|
1338
|
-
this._currentLang();
|
|
1339
|
-
return this._translate.instant('i18n_z_ui_table_filter_max');
|
|
1340
|
-
}, ...(ngDevMode ? [{ debugName: "maxPlaceholder" }] : []));
|
|
1341
|
-
searchPlaceholder = computed(() => {
|
|
1342
|
-
this._currentLang();
|
|
1343
|
-
return this._translate.instant('i18n_z_ui_table_filter_search');
|
|
1344
|
-
}, ...(ngDevMode ? [{ debugName: "searchPlaceholder" }] : []));
|
|
1345
|
-
allLabel = computed(() => {
|
|
1346
|
-
this._currentLang();
|
|
1347
|
-
return this._translate.instant('i18n_z_ui_table_filter_all');
|
|
1348
|
-
}, ...(ngDevMode ? [{ debugName: "allLabel" }] : []));
|
|
1349
|
-
selectOptions = computed(() => {
|
|
1350
|
-
const columnDef = this._columnDef();
|
|
1351
|
-
const allOption = { label: this.allLabel(), value: '' };
|
|
1352
|
-
if (columnDef.filterOptions && columnDef.filterOptions.length > 0) {
|
|
1353
|
-
return [allOption, ...columnDef.filterOptions];
|
|
1354
|
-
}
|
|
1355
|
-
const uniqueValues = Array.from(this.zColumn().getFacetedUniqueValues().keys()).sort().slice(0, 5000);
|
|
1356
|
-
return [allOption, ...uniqueValues.map(v => ({ label: String(v), value: String(v) }))];
|
|
1357
|
-
}, ...(ngDevMode ? [{ debugName: "selectOptions" }] : []));
|
|
1358
|
-
ngOnDestroy() {
|
|
1359
|
-
if (this._debounceTimeout) {
|
|
1360
|
-
clearTimeout(this._debounceTimeout);
|
|
1361
|
-
}
|
|
1362
|
-
}
|
|
1363
|
-
onMinChangeDebounced(value) {
|
|
1364
|
-
this._debounce(() => {
|
|
1365
|
-
const numValue = value === null || value === '' ? undefined : Number(value);
|
|
1366
|
-
this.zColumn().setFilterValue((old) => [numValue, old?.[1]]);
|
|
1367
|
-
});
|
|
1368
|
-
}
|
|
1369
|
-
onMaxChangeDebounced(value) {
|
|
1370
|
-
this._debounce(() => {
|
|
1371
|
-
const numValue = value === null || value === '' ? undefined : Number(value);
|
|
1372
|
-
this.zColumn().setFilterValue((old) => [old?.[0], numValue]);
|
|
1373
|
-
});
|
|
1374
|
-
}
|
|
1375
|
-
onTextChangeDebounced(value) {
|
|
1376
|
-
this._debounce(() => {
|
|
1377
|
-
this.zColumn().setFilterValue(value);
|
|
1378
|
-
});
|
|
1379
|
-
}
|
|
1380
|
-
onSelectChange(value) {
|
|
1381
|
-
if (typeof value === 'object' && value !== null && 'value' in value) {
|
|
1382
|
-
this.zColumn().setFilterValue(value.value || undefined);
|
|
1383
|
-
return;
|
|
1384
|
-
}
|
|
1385
|
-
this.zColumn().setFilterValue(value || undefined);
|
|
1386
|
-
}
|
|
1387
|
-
_debounce(fn) {
|
|
1388
|
-
if (this._debounceTimeout) {
|
|
1389
|
-
clearTimeout(this._debounceTimeout);
|
|
1390
|
-
}
|
|
1391
|
-
this._debounceTimeout = setTimeout(fn, this._debounceMs);
|
|
1392
|
-
}
|
|
1393
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: ZTableFilterComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
1394
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.6", type: ZTableFilterComponent, isStandalone: true, selector: "z-table-filter", inputs: { zColumn: { classPropertyName: "zColumn", publicName: "zColumn", isSignal: true, isRequired: true, transformFunction: null }, zTable: { classPropertyName: "zTable", publicName: "zTable", isSignal: true, isRequired: true, transformFunction: null } }, host: { classAttribute: "z-table-filter block" }, ngImport: i0, template: `
|
|
1395
|
-
@switch (filterType()) {
|
|
1396
|
-
@case ('range') {
|
|
1397
|
-
<div class="flex gap-1">
|
|
1398
|
-
<z-input
|
|
1399
|
-
zType="number"
|
|
1400
|
-
zSize="sm"
|
|
1401
|
-
[zPlaceholder]="minPlaceholder()"
|
|
1402
|
-
[ngModel]="rangeMinValue()"
|
|
1403
|
-
(ngModelChange)="onMinChangeDebounced($event)"
|
|
1404
|
-
class="min-w-0 flex-1"
|
|
1405
|
-
/>
|
|
1406
|
-
<z-input
|
|
1407
|
-
zType="number"
|
|
1408
|
-
zSize="sm"
|
|
1409
|
-
[zPlaceholder]="maxPlaceholder()"
|
|
1410
|
-
[ngModel]="rangeMaxValue()"
|
|
1411
|
-
(ngModelChange)="onMaxChangeDebounced($event)"
|
|
1412
|
-
class="min-w-0 flex-1"
|
|
1413
|
-
/>
|
|
1414
|
-
</div>
|
|
1415
|
-
}
|
|
1416
|
-
@case ('select') {
|
|
1417
|
-
<z-select
|
|
1418
|
-
zSize="sm"
|
|
1419
|
-
[zOptions]="selectOptions()"
|
|
1420
|
-
[ngModel]="columnFilterValue()?.toString() ?? ''"
|
|
1421
|
-
[zPlaceholder]="allLabel()"
|
|
1422
|
-
(ngModelChange)="onSelectChange($event)"
|
|
1423
|
-
class="w-full min-w-0"
|
|
1424
|
-
/>
|
|
1425
|
-
}
|
|
1426
|
-
@case ('number') {
|
|
1427
|
-
<z-input
|
|
1428
|
-
zType="number"
|
|
1429
|
-
zSize="sm"
|
|
1430
|
-
zAllowClear
|
|
1431
|
-
[zPlaceholder]="searchPlaceholder()"
|
|
1432
|
-
[ngModel]="columnFilterValue()?.toString() ?? ''"
|
|
1433
|
-
(ngModelChange)="onTextChangeDebounced($event)"
|
|
1434
|
-
class="w-full"
|
|
1435
|
-
/>
|
|
1436
|
-
}
|
|
1437
|
-
@default {
|
|
1438
|
-
<z-input
|
|
1439
|
-
zType="text"
|
|
1440
|
-
zSize="sm"
|
|
1441
|
-
zAllowClear
|
|
1442
|
-
[zPlaceholder]="searchPlaceholder()"
|
|
1443
|
-
[ngModel]="(columnFilterValue() ?? '').toString()"
|
|
1444
|
-
(ngModelChange)="onTextChangeDebounced($event)"
|
|
1445
|
-
class="w-full"
|
|
1446
|
-
/>
|
|
1447
|
-
}
|
|
1789
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: ZTableRowPipe, decorators: [{
|
|
1790
|
+
type: Pipe,
|
|
1791
|
+
args: [{
|
|
1792
|
+
name: 'zTableRow',
|
|
1793
|
+
standalone: true,
|
|
1794
|
+
pure: true,
|
|
1795
|
+
}]
|
|
1796
|
+
}] });
|
|
1797
|
+
|
|
1798
|
+
class ZTableSpanPipe {
|
|
1799
|
+
transform(item, columns, property, allRows) {
|
|
1800
|
+
const columnConfig = findColumnConfig(item.column.id, columns);
|
|
1801
|
+
switch (property) {
|
|
1802
|
+
case 'cellRowSpan':
|
|
1803
|
+
return calculateCellRowSpan(item, columnConfig, allRows ?? []);
|
|
1804
|
+
case 'cellColSpan':
|
|
1805
|
+
return calculateCellColSpan(item, columnConfig);
|
|
1806
|
+
case 'headerRowSpan':
|
|
1807
|
+
return calculateHeaderRowSpan(item, columnConfig, columns);
|
|
1808
|
+
case 'headerColSpan':
|
|
1809
|
+
return calculateHeaderColSpan(item, columnConfig);
|
|
1810
|
+
case 'footerRowSpan':
|
|
1811
|
+
return calculateFooterRowSpan(item, columnConfig);
|
|
1812
|
+
case 'footerColSpan':
|
|
1813
|
+
return calculateFooterColSpan(item, columnConfig);
|
|
1814
|
+
case 'shouldRender':
|
|
1815
|
+
return shouldCellRenderUtil(item, columnConfig, allRows ?? []);
|
|
1816
|
+
default:
|
|
1817
|
+
return null;
|
|
1818
|
+
}
|
|
1448
1819
|
}
|
|
1449
|
-
|
|
1820
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: ZTableSpanPipe, deps: [], target: i0.ɵɵFactoryTarget.Pipe });
|
|
1821
|
+
static ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "21.0.6", ngImport: i0, type: ZTableSpanPipe, isStandalone: true, name: "zTableSpan" });
|
|
1450
1822
|
}
|
|
1451
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type:
|
|
1452
|
-
type:
|
|
1823
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: ZTableSpanPipe, decorators: [{
|
|
1824
|
+
type: Pipe,
|
|
1453
1825
|
args: [{
|
|
1454
|
-
|
|
1455
|
-
imports: [FormsModule, ZInputComponent, ZSelectComponent],
|
|
1826
|
+
name: 'zTableSpan',
|
|
1456
1827
|
standalone: true,
|
|
1457
|
-
|
|
1458
|
-
|
|
1459
|
-
|
|
1460
|
-
|
|
1461
|
-
|
|
1462
|
-
|
|
1463
|
-
|
|
1464
|
-
[zPlaceholder]="minPlaceholder()"
|
|
1465
|
-
[ngModel]="rangeMinValue()"
|
|
1466
|
-
(ngModelChange)="onMinChangeDebounced($event)"
|
|
1467
|
-
class="min-w-0 flex-1"
|
|
1468
|
-
/>
|
|
1469
|
-
<z-input
|
|
1470
|
-
zType="number"
|
|
1471
|
-
zSize="sm"
|
|
1472
|
-
[zPlaceholder]="maxPlaceholder()"
|
|
1473
|
-
[ngModel]="rangeMaxValue()"
|
|
1474
|
-
(ngModelChange)="onMaxChangeDebounced($event)"
|
|
1475
|
-
class="min-w-0 flex-1"
|
|
1476
|
-
/>
|
|
1477
|
-
</div>
|
|
1478
|
-
}
|
|
1479
|
-
@case ('select') {
|
|
1480
|
-
<z-select
|
|
1481
|
-
zSize="sm"
|
|
1482
|
-
[zOptions]="selectOptions()"
|
|
1483
|
-
[ngModel]="columnFilterValue()?.toString() ?? ''"
|
|
1484
|
-
[zPlaceholder]="allLabel()"
|
|
1485
|
-
(ngModelChange)="onSelectChange($event)"
|
|
1486
|
-
class="w-full min-w-0"
|
|
1487
|
-
/>
|
|
1488
|
-
}
|
|
1489
|
-
@case ('number') {
|
|
1490
|
-
<z-input
|
|
1491
|
-
zType="number"
|
|
1492
|
-
zSize="sm"
|
|
1493
|
-
zAllowClear
|
|
1494
|
-
[zPlaceholder]="searchPlaceholder()"
|
|
1495
|
-
[ngModel]="columnFilterValue()?.toString() ?? ''"
|
|
1496
|
-
(ngModelChange)="onTextChangeDebounced($event)"
|
|
1497
|
-
class="w-full"
|
|
1498
|
-
/>
|
|
1499
|
-
}
|
|
1500
|
-
@default {
|
|
1501
|
-
<z-input
|
|
1502
|
-
zType="text"
|
|
1503
|
-
zSize="sm"
|
|
1504
|
-
zAllowClear
|
|
1505
|
-
[zPlaceholder]="searchPlaceholder()"
|
|
1506
|
-
[ngModel]="(columnFilterValue() ?? '').toString()"
|
|
1507
|
-
(ngModelChange)="onTextChangeDebounced($event)"
|
|
1508
|
-
class="w-full"
|
|
1509
|
-
/>
|
|
1510
|
-
}
|
|
1828
|
+
pure: true,
|
|
1829
|
+
}]
|
|
1830
|
+
}] });
|
|
1831
|
+
|
|
1832
|
+
class ZTableIsTemplateRefPipe {
|
|
1833
|
+
transform(value) {
|
|
1834
|
+
return value instanceof TemplateRef;
|
|
1511
1835
|
}
|
|
1512
|
-
|
|
1513
|
-
|
|
1514
|
-
|
|
1515
|
-
|
|
1516
|
-
|
|
1836
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: ZTableIsTemplateRefPipe, deps: [], target: i0.ɵɵFactoryTarget.Pipe });
|
|
1837
|
+
static ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "21.0.6", ngImport: i0, type: ZTableIsTemplateRefPipe, isStandalone: true, name: "zTableIsTemplateRef" });
|
|
1838
|
+
}
|
|
1839
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: ZTableIsTemplateRefPipe, decorators: [{
|
|
1840
|
+
type: Pipe,
|
|
1841
|
+
args: [{
|
|
1842
|
+
name: 'zTableIsTemplateRef',
|
|
1843
|
+
standalone: true,
|
|
1844
|
+
pure: true,
|
|
1517
1845
|
}]
|
|
1518
|
-
}]
|
|
1846
|
+
}] });
|
|
1519
1847
|
|
|
1520
|
-
class
|
|
1521
|
-
|
|
1522
|
-
|
|
1523
|
-
|
|
1524
|
-
|
|
1525
|
-
|
|
1526
|
-
parts = computed(() => parseIconString(this.zText()), ...(ngDevMode ? [{ debugName: "parts" }] : []));
|
|
1527
|
-
tooltipContent = computed(() => {
|
|
1528
|
-
const tooltip = this.zTooltip();
|
|
1529
|
-
if (tooltip) {
|
|
1530
|
-
if (typeof tooltip === 'string') {
|
|
1531
|
-
return stripIconSyntax(tooltip);
|
|
1532
|
-
}
|
|
1533
|
-
if (tooltip.content && typeof tooltip.content === 'string') {
|
|
1534
|
-
return stripIconSyntax(tooltip.content);
|
|
1535
|
-
}
|
|
1536
|
-
}
|
|
1537
|
-
return stripIconSyntax(this.zText());
|
|
1538
|
-
}, ...(ngDevMode ? [{ debugName: "tooltipContent" }] : []));
|
|
1539
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: ZTableIconTextComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
1540
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.6", type: ZTableIconTextComponent, isStandalone: true, selector: "z-table-icon-text", inputs: { zText: { classPropertyName: "zText", publicName: "zText", isSignal: true, isRequired: false, transformFunction: null }, zTooltip: { classPropertyName: "zTooltip", publicName: "zTooltip", isSignal: true, isRequired: false, transformFunction: null }, zTriggerElement: { classPropertyName: "zTriggerElement", publicName: "zTriggerElement", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: `
|
|
1541
|
-
<span class="z-table-icon-text-inner">
|
|
1542
|
-
@for (part of parts(); track $index) {
|
|
1543
|
-
@if (part.type === 'icon') {
|
|
1544
|
-
<z-icon
|
|
1545
|
-
class="shrink-0"
|
|
1546
|
-
[zType]="$any(part.value)"
|
|
1547
|
-
[zSize]="$any(part.size ?? 14)"
|
|
1548
|
-
[zStrokeWidth]="part.strokeWidth ?? 1.5"
|
|
1549
|
-
[ngClass]="part.class ?? ''"
|
|
1550
|
-
/>
|
|
1551
|
-
} @else {
|
|
1552
|
-
<span
|
|
1553
|
-
class="truncate select-text"
|
|
1554
|
-
z-tooltip
|
|
1555
|
-
[zContent]="tooltipContent()"
|
|
1556
|
-
[zTriggerElement]="triggerEl()"
|
|
1557
|
-
[innerHTML]="part.value | zSafeHtml"
|
|
1558
|
-
></span>
|
|
1559
|
-
}
|
|
1560
|
-
}
|
|
1561
|
-
</span>
|
|
1562
|
-
`, isInline: true, styles: [":host{display:inline-flex;align-items:center;min-width:0;max-width:100%}.z-table-icon-text-inner{display:inline-flex;align-items:center;gap:4px;line-height:1.2;min-width:0;max-width:100%}span.truncate{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}\n"], dependencies: [{ kind: "directive", type: NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "component", type: ZIconComponent, selector: "z-icon, [z-icon]", inputs: ["class", "zType", "zSize", "zStrokeWidth", "zSvg"] }, { 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: "pipe", type: ZSafeHtmlPipe, name: "zSafeHtml" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
1848
|
+
class ZTableBodyCellRenderPipe {
|
|
1849
|
+
transform(cell, allCells, columns) {
|
|
1850
|
+
return shouldBodyCellRenderColSpan(cell, allCells, columns);
|
|
1851
|
+
}
|
|
1852
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: ZTableBodyCellRenderPipe, deps: [], target: i0.ɵɵFactoryTarget.Pipe });
|
|
1853
|
+
static ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "21.0.6", ngImport: i0, type: ZTableBodyCellRenderPipe, isStandalone: true, name: "zTableBodyCellRender" });
|
|
1563
1854
|
}
|
|
1564
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type:
|
|
1565
|
-
type:
|
|
1566
|
-
args: [{
|
|
1567
|
-
|
|
1568
|
-
|
|
1569
|
-
|
|
1570
|
-
|
|
1571
|
-
|
|
1572
|
-
|
|
1573
|
-
|
|
1574
|
-
|
|
1575
|
-
|
|
1576
|
-
|
|
1577
|
-
|
|
1578
|
-
|
|
1579
|
-
|
|
1580
|
-
|
|
1581
|
-
|
|
1582
|
-
[
|
|
1583
|
-
|
|
1584
|
-
|
|
1585
|
-
|
|
1586
|
-
|
|
1587
|
-
|
|
1588
|
-
|
|
1589
|
-
|
|
1855
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: ZTableBodyCellRenderPipe, decorators: [{
|
|
1856
|
+
type: Pipe,
|
|
1857
|
+
args: [{
|
|
1858
|
+
name: 'zTableBodyCellRender',
|
|
1859
|
+
standalone: true,
|
|
1860
|
+
pure: true,
|
|
1861
|
+
}]
|
|
1862
|
+
}] });
|
|
1863
|
+
|
|
1864
|
+
class ZTableFooterRenderPipe {
|
|
1865
|
+
transform(header, allHeaders, columns) {
|
|
1866
|
+
return shouldFooterRender(header, allHeaders, columns);
|
|
1867
|
+
}
|
|
1868
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: ZTableFooterRenderPipe, deps: [], target: i0.ɵɵFactoryTarget.Pipe });
|
|
1869
|
+
static ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "21.0.6", ngImport: i0, type: ZTableFooterRenderPipe, isStandalone: true, name: "zTableFooterRender" });
|
|
1870
|
+
}
|
|
1871
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: ZTableFooterRenderPipe, decorators: [{
|
|
1872
|
+
type: Pipe,
|
|
1873
|
+
args: [{
|
|
1874
|
+
name: 'zTableFooterRender',
|
|
1875
|
+
standalone: true,
|
|
1876
|
+
pure: true,
|
|
1877
|
+
}]
|
|
1878
|
+
}] });
|
|
1879
|
+
|
|
1880
|
+
class ZTableHeaderRenderPipe {
|
|
1881
|
+
transform(header, allHeaders, columns) {
|
|
1882
|
+
return shouldHeaderRender(header, allHeaders, columns);
|
|
1883
|
+
}
|
|
1884
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: ZTableHeaderRenderPipe, deps: [], target: i0.ɵɵFactoryTarget.Pipe });
|
|
1885
|
+
static ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "21.0.6", ngImport: i0, type: ZTableHeaderRenderPipe, isStandalone: true, name: "zTableHeaderRender" });
|
|
1886
|
+
}
|
|
1887
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: ZTableHeaderRenderPipe, decorators: [{
|
|
1888
|
+
type: Pipe,
|
|
1889
|
+
args: [{
|
|
1890
|
+
name: 'zTableHeaderRender',
|
|
1891
|
+
standalone: true,
|
|
1892
|
+
pure: true,
|
|
1893
|
+
}]
|
|
1894
|
+
}] });
|
|
1590
1895
|
|
|
1591
1896
|
/* eslint-disable @stylistic/indent */
|
|
1897
|
+
const Z_TABLE_DEFAULT_ACTION_BUTTON_WIDTH = 32;
|
|
1592
1898
|
class ZTableComponent {
|
|
1593
1899
|
zConfig = input({ data: [], columns: [], mode: 'local' }, ...(ngDevMode ? [{ debugName: "zConfig" }] : []));
|
|
1594
1900
|
zLoading = input(false, ...(ngDevMode ? [{ debugName: "zLoading" }] : []));
|
|
@@ -1596,7 +1902,6 @@ class ZTableComponent {
|
|
|
1596
1902
|
zChange = output();
|
|
1597
1903
|
zControl = output();
|
|
1598
1904
|
_destroyRef = inject(DestroyRef);
|
|
1599
|
-
_ngZone = inject(NgZone);
|
|
1600
1905
|
_isSyncingScroll = signal(false, ...(ngDevMode ? [{ debugName: "_isSyncingScroll" }] : []));
|
|
1601
1906
|
_savedScrollLeft = signal(0, ...(ngDevMode ? [{ debugName: "_savedScrollLeft" }] : []));
|
|
1602
1907
|
_resizeObserver = null;
|
|
@@ -1668,6 +1973,46 @@ class ZTableComponent {
|
|
|
1668
1973
|
const columns = this.zConfig().columns ?? [];
|
|
1669
1974
|
return columns.some(c => c.id === 'expand');
|
|
1670
1975
|
}, ...(ngDevMode ? [{ debugName: "hasExpandColumn" }] : []));
|
|
1976
|
+
actionColumnInfo = computed(() => {
|
|
1977
|
+
const columns = this.zConfig().columns ?? [];
|
|
1978
|
+
const findActionColumn = (cols) => {
|
|
1979
|
+
for (const col of cols) {
|
|
1980
|
+
if (isBodyConfig(col.body) && col.body.actions && col.body.actions.actions?.length > 0) {
|
|
1981
|
+
return { columnId: col.id, config: col.body.actions };
|
|
1982
|
+
}
|
|
1983
|
+
if (col.columns?.length) {
|
|
1984
|
+
const found = findActionColumn(col.columns);
|
|
1985
|
+
if (found) {
|
|
1986
|
+
return found;
|
|
1987
|
+
}
|
|
1988
|
+
}
|
|
1989
|
+
}
|
|
1990
|
+
return null;
|
|
1991
|
+
};
|
|
1992
|
+
const actionCol = findActionColumn(columns);
|
|
1993
|
+
if (!actionCol) {
|
|
1994
|
+
return null;
|
|
1995
|
+
}
|
|
1996
|
+
const { config } = actionCol;
|
|
1997
|
+
let { width } = config;
|
|
1998
|
+
if (!width) {
|
|
1999
|
+
const maxVisible = config.maxVisible ?? 3;
|
|
2000
|
+
const totalActions = config.actions.filter(a => !a.hidden).length;
|
|
2001
|
+
const visibleCount = Math.min(totalActions, maxVisible);
|
|
2002
|
+
const hasOverflow = totalActions > maxVisible;
|
|
2003
|
+
const buttonCount = hasOverflow ? visibleCount + 1 : visibleCount;
|
|
2004
|
+
const buttonWidth = Z_TABLE_DEFAULT_ACTION_BUTTON_WIDTH;
|
|
2005
|
+
const gap = 4;
|
|
2006
|
+
const padding = 16;
|
|
2007
|
+
width = buttonCount * buttonWidth + (buttonCount - 1) * gap + padding;
|
|
2008
|
+
}
|
|
2009
|
+
return {
|
|
2010
|
+
columnId: actionCol.columnId,
|
|
2011
|
+
config: actionCol.config,
|
|
2012
|
+
width,
|
|
2013
|
+
};
|
|
2014
|
+
}, ...(ngDevMode ? [{ debugName: "actionColumnInfo" }] : []));
|
|
2015
|
+
hasActionColumn = computed(() => this.actionColumnInfo() !== null, ...(ngDevMode ? [{ debugName: "hasActionColumn" }] : []));
|
|
1671
2016
|
hasFiltering = computed(() => {
|
|
1672
2017
|
const columns = this.zConfig().columns ?? [];
|
|
1673
2018
|
const checkFilter = (col) => {
|
|
@@ -1683,7 +2028,14 @@ class ZTableComponent {
|
|
|
1683
2028
|
return columns.some(c => checkFilter(c));
|
|
1684
2029
|
}, ...(ngDevMode ? [{ debugName: "hasFiltering" }] : []));
|
|
1685
2030
|
hasSorting = computed(() => {
|
|
1686
|
-
const
|
|
2031
|
+
const config = this.zConfig();
|
|
2032
|
+
if (config.enableColumnSorting === false) {
|
|
2033
|
+
return false;
|
|
2034
|
+
}
|
|
2035
|
+
if (config.enableColumnSorting === true) {
|
|
2036
|
+
return true;
|
|
2037
|
+
}
|
|
2038
|
+
const columns = config.columns ?? [];
|
|
1687
2039
|
const checkSorting = (col) => {
|
|
1688
2040
|
const sortConfig = typeof col.sort === 'boolean' ? { enabled: col.sort } : col.sort;
|
|
1689
2041
|
if (sortConfig?.enabled) {
|
|
@@ -1708,18 +2060,16 @@ class ZTableComponent {
|
|
|
1708
2060
|
}
|
|
1709
2061
|
const hasExpandColumn = cols.some(c => c.id === 'expand');
|
|
1710
2062
|
const hasSelectColumn = cols.some(c => c.id === 'select');
|
|
1711
|
-
const
|
|
1712
|
-
if (
|
|
2063
|
+
const needsRowPinActionsColumn = this.zConfig().enableRowPinning && !hasExpandColumn && !hasSelectColumn && !this.hasBodyRowSpan();
|
|
2064
|
+
if (needsRowPinActionsColumn) {
|
|
1713
2065
|
cols = [
|
|
1714
2066
|
{
|
|
1715
|
-
id: '
|
|
2067
|
+
id: 'actionRowPin',
|
|
1716
2068
|
header: '',
|
|
1717
2069
|
cell: '',
|
|
1718
2070
|
size: 30,
|
|
1719
2071
|
minSize: 30,
|
|
1720
2072
|
maxSize: 30,
|
|
1721
|
-
enableSorting: false,
|
|
1722
|
-
enableFiltering: false,
|
|
1723
2073
|
enableResizing: false,
|
|
1724
2074
|
enableHiding: false,
|
|
1725
2075
|
},
|
|
@@ -1734,12 +2084,20 @@ class ZTableComponent {
|
|
|
1734
2084
|
colDef.maxSize = 50;
|
|
1735
2085
|
colDef.enableResizing = false;
|
|
1736
2086
|
}
|
|
1737
|
-
if (colDef.id === 'expand' || colDef.id === '
|
|
2087
|
+
if (colDef.id === 'expand' || colDef.id === 'actionRowPin') {
|
|
1738
2088
|
colDef.size = 50;
|
|
1739
2089
|
colDef.minSize = 50;
|
|
1740
2090
|
colDef.maxSize = 50;
|
|
1741
2091
|
colDef.enableResizing = false;
|
|
1742
2092
|
}
|
|
2093
|
+
const actionInfo = this.actionColumnInfo();
|
|
2094
|
+
if (actionInfo && colDef.id === actionInfo.columnId) {
|
|
2095
|
+
colDef.size = actionInfo.width;
|
|
2096
|
+
colDef.minSize = actionInfo.width;
|
|
2097
|
+
colDef.maxSize = actionInfo.width;
|
|
2098
|
+
colDef.enableResizing = false;
|
|
2099
|
+
colDef.enableSorting = false;
|
|
2100
|
+
}
|
|
1743
2101
|
return colDef;
|
|
1744
2102
|
});
|
|
1745
2103
|
}, ...(ngDevMode ? [{ debugName: "_columns" }] : []));
|
|
@@ -2484,6 +2842,12 @@ class ZTableComponent {
|
|
|
2484
2842
|
toggleHeaderFooterShadow() {
|
|
2485
2843
|
this.showHeaderFooterShadow.update(v => !v);
|
|
2486
2844
|
}
|
|
2845
|
+
onActionClick(event) {
|
|
2846
|
+
this.zChange.emit({
|
|
2847
|
+
type: 'action',
|
|
2848
|
+
data: event,
|
|
2849
|
+
});
|
|
2850
|
+
}
|
|
2487
2851
|
openSettingsDrawer() {
|
|
2488
2852
|
if (this.columnOrder().length === 0) {
|
|
2489
2853
|
this.columnOrder.set(this.table.getAllLeafColumns().map(col => col.id));
|
|
@@ -2736,7 +3100,7 @@ class ZTableComponent {
|
|
|
2736
3100
|
});
|
|
2737
3101
|
}
|
|
2738
3102
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: ZTableComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
2739
|
-
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" }, 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]=\"header.column.id === 'actions'\"\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 if (header.column.id === 'actions') {\n <!-- Empty header for actions column -->\n <div class=\"flex items-center justify-center\"></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 <ng-template #colOptionsPopoverContent>\n <div class=\"flex flex-col gap-0.5 p-0.5\">\n @if (zConfig().enableColumnOrdering && columnEnableOrdering) {\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 (\n zConfig().enableColumnOrdering &&\n columnEnableOrdering &&\n header.column.getCanPin() &&\n zConfig().enableColumnPinning &&\n columnEnablePinning\n ) {\n <div class=\"border-border my-0.5 border-t\"></div>\n }\n @if (header.column.getCanPin() && zConfig().enableColumnPinning && columnEnablePinning) {\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() && zConfig().enableColumnPinning && columnEnablePinning) ||\n (zConfig().enableColumnOrdering && columnEnableOrdering);\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 <ng-container\n *ngTemplateOutlet=\"headerContent; context: { $implicit: header.getContext() }\" />\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 } @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 | zSafeHtml\"></span>\n }\n </ng-container>\n <!-- Sort Icon (clickable) - hidden when table has rowSpan columns -->\n @if (header.column.getCanSort() && !hasBodyRowSpan()) {\n <span\n class=\"shrink-0 cursor-pointer text-gray-500 hover:text-gray-700\"\n (click)=\"\n $event.stopPropagation(); handleSort($event, header.column.getToggleSortingHandler())\n \">\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 <!-- Dropdown indicator when has options -->\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 </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 (mousedown)=\"header.getResizeHandler()($event)\"\n (touchstart)=\"header.getResizeHandler()($event)\"></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-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 {\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]=\"cell.column.id === 'actions'\"\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 {\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 {\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 <ng-container\n *ngTemplateOutlet=\"footerContent; context: { $implicit: footer.getContext() }\" />\n } @else if (footerContent | zTableHasIcon) {\n <!-- Icon syntax rendering -->\n <z-table-icon-text\n [zText]=\"footerContent\"\n [zTooltip]=\"\n footer.column.id | zTableColumnConfig: zConfig().columns : 'footerTooltip'\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 {{ footerContent }}\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 {{ configFooterContent }}\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}.z-thead-wrapper th.z-sticky-left-last:after:where(.dark,.dark *),.z-thead-wrapper td.z-sticky-left-last:after:where(.dark,.dark *),.z-tbody-wrapper th.z-sticky-left-last:after:where(.dark,.dark *),.z-tbody-wrapper td.z-sticky-left-last:after:where(.dark,.dark *),.z-tfoot-wrapper th.z-sticky-left-last:after:where(.dark,.dark *),.z-tfoot-wrapper td.z-sticky-left-last:after:where(.dark,.dark *){box-shadow:inset 10px 0 8px -8px #0000004d}.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{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 th.z-sticky-right-first:before:where(.dark,.dark *),.z-thead-wrapper td.z-sticky-right-first:before:where(.dark,.dark *),.z-tbody-wrapper th.z-sticky-right-first:before:where(.dark,.dark *),.z-tbody-wrapper td.z-sticky-right-first:before:where(.dark,.dark *),.z-tfoot-wrapper th.z-sticky-right-first:before:where(.dark,.dark *),.z-tfoot-wrapper td.z-sticky-right-first:before:where(.dark,.dark *){box-shadow:inset -10px 0 8px -8px #0000004d}.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}.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: ZTableRowHoverDirective, selector: "[z-table-row-hover], [zTableRowHover]", inputs: ["zTableRowHover"] }, { kind: "component", type: ZTableIconTextComponent, selector: "z-table-icon-text", inputs: ["zText", "zTooltip", "zTriggerElement"] }, { 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 });
|
|
3103
|
+
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" }, 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 if (header.column.id === 'actions' || actionColumnInfo()?.columnId === header.column.id) {\n <div class=\"flex items-center justify-center\">\n @if (actionColumnInfo()?.config?.header) {\n <span class=\"text-muted-foreground text-xs\">{{ actionColumnInfo()?.config?.header }}</span>\n }\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 | 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 }}\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 {{ configFooterContent }}\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 });
|
|
2740
3104
|
}
|
|
2741
3105
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: ZTableComponent, decorators: [{
|
|
2742
3106
|
type: Component,
|
|
@@ -2761,6 +3125,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImpor
|
|
|
2761
3125
|
ZPopoverDirective,
|
|
2762
3126
|
ZButtonComponent,
|
|
2763
3127
|
ZTooltipDirective,
|
|
3128
|
+
ZTableResizerDirective,
|
|
2764
3129
|
ZTableRowHoverDirective,
|
|
2765
3130
|
ZTableIsTemplateRefPipe,
|
|
2766
3131
|
ZTableHasIconPipe,
|
|
@@ -2779,16 +3144,17 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImpor
|
|
|
2779
3144
|
ZTableHeaderRenderPipe,
|
|
2780
3145
|
ZTableBodyCellRenderPipe,
|
|
2781
3146
|
ZTableIconTextComponent,
|
|
3147
|
+
ZTableActionsComponent,
|
|
2782
3148
|
ZSafeHtmlPipe,
|
|
2783
3149
|
TranslatePipe,
|
|
2784
3150
|
], standalone: true, providers: [TranslatePipe], changeDetection: ChangeDetectionStrategy.OnPush, host: {
|
|
2785
3151
|
class: 'z-table block relative',
|
|
2786
|
-
}, 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]=\"header.column.id === 'actions'\"\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 if (header.column.id === 'actions') {\n <!-- Empty header for actions column -->\n <div class=\"flex items-center justify-center\"></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 <ng-template #colOptionsPopoverContent>\n <div class=\"flex flex-col gap-0.5 p-0.5\">\n @if (zConfig().enableColumnOrdering && columnEnableOrdering) {\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 (\n zConfig().enableColumnOrdering &&\n columnEnableOrdering &&\n header.column.getCanPin() &&\n zConfig().enableColumnPinning &&\n columnEnablePinning\n ) {\n <div class=\"border-border my-0.5 border-t\"></div>\n }\n @if (header.column.getCanPin() && zConfig().enableColumnPinning && columnEnablePinning) {\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() && zConfig().enableColumnPinning && columnEnablePinning) ||\n (zConfig().enableColumnOrdering && columnEnableOrdering);\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 <ng-container\n *ngTemplateOutlet=\"headerContent; context: { $implicit: header.getContext() }\" />\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 } @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 | zSafeHtml\"></span>\n }\n </ng-container>\n <!-- Sort Icon (clickable) - hidden when table has rowSpan columns -->\n @if (header.column.getCanSort() && !hasBodyRowSpan()) {\n <span\n class=\"shrink-0 cursor-pointer text-gray-500 hover:text-gray-700\"\n (click)=\"\n $event.stopPropagation(); handleSort($event, header.column.getToggleSortingHandler())\n \">\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 <!-- Dropdown indicator when has options -->\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 </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 (mousedown)=\"header.getResizeHandler()($event)\"\n (touchstart)=\"header.getResizeHandler()($event)\"></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-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 {\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]=\"cell.column.id === 'actions'\"\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 {\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 {\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 <ng-container\n *ngTemplateOutlet=\"footerContent; context: { $implicit: footer.getContext() }\" />\n } @else if (footerContent | zTableHasIcon) {\n <!-- Icon syntax rendering -->\n <z-table-icon-text\n [zText]=\"footerContent\"\n [zTooltip]=\"\n footer.column.id | zTableColumnConfig: zConfig().columns : 'footerTooltip'\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 {{ footerContent }}\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 {{ configFooterContent }}\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}.z-thead-wrapper th.z-sticky-left-last:after:where(.dark,.dark *),.z-thead-wrapper td.z-sticky-left-last:after:where(.dark,.dark *),.z-tbody-wrapper th.z-sticky-left-last:after:where(.dark,.dark *),.z-tbody-wrapper td.z-sticky-left-last:after:where(.dark,.dark *),.z-tfoot-wrapper th.z-sticky-left-last:after:where(.dark,.dark *),.z-tfoot-wrapper td.z-sticky-left-last:after:where(.dark,.dark *){box-shadow:inset 10px 0 8px -8px #0000004d}.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{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 th.z-sticky-right-first:before:where(.dark,.dark *),.z-thead-wrapper td.z-sticky-right-first:before:where(.dark,.dark *),.z-tbody-wrapper th.z-sticky-right-first:before:where(.dark,.dark *),.z-tbody-wrapper td.z-sticky-right-first:before:where(.dark,.dark *),.z-tfoot-wrapper th.z-sticky-right-first:before:where(.dark,.dark *),.z-tfoot-wrapper td.z-sticky-right-first:before:where(.dark,.dark *){box-shadow:inset -10px 0 8px -8px #0000004d}.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}.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"] }]
|
|
3152
|
+
}, 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 if (header.column.id === 'actions' || actionColumnInfo()?.columnId === header.column.id) {\n <div class=\"flex items-center justify-center\">\n @if (actionColumnInfo()?.config?.header) {\n <span class=\"text-muted-foreground text-xs\">{{ actionColumnInfo()?.config?.header }}</span>\n }\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 | 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 }}\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 {{ configFooterContent }}\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"] }]
|
|
2787
3153
|
}], 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 }] }] } });
|
|
2788
3154
|
|
|
2789
3155
|
/**
|
|
2790
3156
|
* Generated bundle index. Do not edit.
|
|
2791
3157
|
*/
|
|
2792
3158
|
|
|
2793
|
-
export { ZTableComponent, ZTableFilterComponent, ZTableIconTextComponent, columnConfigToColumnDef, findColumnConfig, getBodyConfig, getFooterConfig, getHeaderConfig, isBodyConfig, isFooterConfig, isHeaderConfig };
|
|
3159
|
+
export { ZTableActionsComponent, ZTableComponent, ZTableFilterComponent, ZTableIconTextComponent, columnConfigToColumnDef, findColumnConfig, getBodyConfig, getFooterConfig, getHeaderConfig, isBodyConfig, isFooterConfig, isHeaderConfig };
|
|
2794
3160
|
//# sourceMappingURL=shival99-z-ui-components-z-table.mjs.map
|