@reforgium/data-grid 1.1.0 → 2.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,14 +1,14 @@
1
1
  import * as i0 from '@angular/core';
2
- import { input, inject, TemplateRef, Directive, numberAttribute, output, computed, Component, signal, effect, Injectable, viewChild, ElementRef, ChangeDetectionStrategy, booleanAttribute, contentChildren, afterRenderEffect, DestroyRef, untracked } from '@angular/core';
2
+ import { input, inject, TemplateRef, Directive, booleanAttribute, contentChild, Component, numberAttribute, InjectionToken, makeEnvironmentProviders, signal, computed, effect, Injectable, viewChild, ElementRef, ChangeDetectionStrategy, output, NgZone, contentChildren, afterRenderEffect, DestroyRef, untracked } from '@angular/core';
3
3
  import { NgTemplateOutlet, DatePipe, DecimalPipe } from '@angular/common';
4
- import { Subscription, fromEvent, auditTime } from 'rxjs';
4
+ import { Subscription, Subject, fromEvent, debounceTime } from 'rxjs';
5
5
 
6
6
  /**
7
7
  * Directive for defining type-specific cell templates in a data grid.
8
8
  *
9
9
  * This directive allows developers to create custom cell renderers for specific data types
10
10
  * within the data grid component. It binds a template to a type identifier, enabling
11
- * the grid to dynamically select and render the appropriate template based on column type.
11
+ * the grid to dynamically select and render the appropriate template based on a column type.
12
12
  *
13
13
  * Example usage:
14
14
  * ```html
@@ -95,6 +95,118 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.1", ngImpor
95
95
  args: [{ selector: 'ng-template[reDataGridHeader]' }]
96
96
  }], propDecorators: { key: [{ type: i0.Input, args: [{ isSignal: true, alias: "reDataGridHeader", required: false }] }] } });
97
97
 
98
+ /**
99
+ * Directive for providing a custom template for data grid rows.
100
+ *
101
+ * Used as a structural directive on `<ng-template>` elements within data grid components.
102
+ * The template receives row data, index, columns, and layout helpers.
103
+ *
104
+ * Example:
105
+ * ```html
106
+ * <ng-template reDataGridRow let-row let-index="index">
107
+ * <div class="my-row">{{ index + 1 }} - {{ row.name }}</div>
108
+ * </ng-template>
109
+ * ```
110
+ *
111
+ * Template context:
112
+ * - `$implicit: Data` — row data
113
+ * - `index: number` — row index
114
+ * - `columns: GridColumn<Data>[]` — visible columns
115
+ * - `rowHeight: number` — row height in pixels
116
+ * - `isSticky: boolean` — sticky row flag
117
+ */
118
+ class DataGridRowDirective {
119
+ tpl = inject((TemplateRef));
120
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.1", ngImport: i0, type: DataGridRowDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive });
121
+ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "21.1.1", type: DataGridRowDirective, isStandalone: true, selector: "ng-template[reDataGridRow]", ngImport: i0 });
122
+ }
123
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.1", ngImport: i0, type: DataGridRowDirective, decorators: [{
124
+ type: Directive,
125
+ args: [{ selector: 'ng-template[reDataGridRow]' }]
126
+ }] });
127
+
128
+ class DataGridDeclarativeHeaderDirective {
129
+ tpl = inject((TemplateRef));
130
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.1", ngImport: i0, type: DataGridDeclarativeHeaderDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive });
131
+ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "21.1.1", type: DataGridDeclarativeHeaderDirective, isStandalone: true, selector: "ng-template[reHeader]", ngImport: i0 });
132
+ }
133
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.1", ngImport: i0, type: DataGridDeclarativeHeaderDirective, decorators: [{
134
+ type: Directive,
135
+ args: [{ selector: 'ng-template[reHeader]' }]
136
+ }] });
137
+ class DataGridDeclarativeCellDirective {
138
+ tpl = inject((TemplateRef));
139
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.1", ngImport: i0, type: DataGridDeclarativeCellDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive });
140
+ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "21.1.1", type: DataGridDeclarativeCellDirective, isStandalone: true, selector: "ng-template[reCell]", ngImport: i0 });
141
+ }
142
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.1", ngImport: i0, type: DataGridDeclarativeCellDirective, decorators: [{
143
+ type: Directive,
144
+ args: [{ selector: 'ng-template[reCell]' }]
145
+ }] });
146
+
147
+ class DataGridDeclarativeColumn {
148
+ key = input.required(...(ngDevMode ? [{ debugName: "key" }] : []));
149
+ header = input(undefined, ...(ngDevMode ? [{ debugName: "header" }] : []));
150
+ align = input(undefined, ...(ngDevMode ? [{ debugName: "align" }] : []));
151
+ sortKey = input(undefined, ...(ngDevMode ? [{ debugName: "sortKey" }] : []));
152
+ sticky = input(...(ngDevMode ? [undefined, { debugName: "sticky" }] : []));
153
+ expandBy = input(undefined, ...(ngDevMode ? [{ debugName: "expandBy" }] : []));
154
+ disabled = input(false, { ...(ngDevMode ? { debugName: "disabled" } : {}), transform: booleanAttribute });
155
+ visible = input(true, { ...(ngDevMode ? { debugName: "visible" } : {}), transform: booleanAttribute });
156
+ width = input(undefined, { ...(ngDevMode ? { debugName: "width" } : {}), transform: toOptionalNumber });
157
+ minWidth = input(undefined, { ...(ngDevMode ? { debugName: "minWidth" } : {}), transform: toOptionalNumber });
158
+ maxWidth = input(undefined, { ...(ngDevMode ? { debugName: "maxWidth" } : {}), transform: toOptionalNumber });
159
+ flex = input(undefined, { ...(ngDevMode ? { debugName: "flex" } : {}), transform: toOptionalNumber });
160
+ type = input(undefined, ...(ngDevMode ? [{ debugName: "type" }] : []));
161
+ typeParams = input(undefined, ...(ngDevMode ? [{ debugName: "typeParams" }] : []));
162
+ defaultValue = input(undefined, ...(ngDevMode ? [{ debugName: "defaultValue" }] : []));
163
+ value = input(undefined, ...(ngDevMode ? [{ debugName: "value" }] : []));
164
+ track = input(undefined, ...(ngDevMode ? [{ debugName: "track" }] : []));
165
+ tooltip = input(undefined, ...(ngDevMode ? [{ debugName: "tooltip" }] : []));
166
+ headerTplRef = contentChild(DataGridDeclarativeHeaderDirective, ...(ngDevMode ? [{ debugName: "headerTplRef" }] : []));
167
+ cellTplRef = contentChild(DataGridDeclarativeCellDirective, ...(ngDevMode ? [{ debugName: "cellTplRef" }] : []));
168
+ toDeclarativeColumn() {
169
+ return {
170
+ key: this.key(),
171
+ header: this.header(),
172
+ headerTemplate: this.headerTplRef()?.tpl,
173
+ cellTemplate: this.cellTplRef()?.tpl,
174
+ align: this.align(),
175
+ sortKey: this.sortKey(),
176
+ sticky: this.sticky(),
177
+ expandBy: this.expandBy(),
178
+ disabled: this.disabled(),
179
+ visible: this.visible(),
180
+ width: this.width(),
181
+ minWidth: this.minWidth(),
182
+ maxWidth: this.maxWidth(),
183
+ flex: this.flex(),
184
+ type: this.type(),
185
+ typeParams: this.typeParams(),
186
+ defaultValue: this.defaultValue(),
187
+ value: this.value(),
188
+ track: this.track(),
189
+ tooltip: this.tooltip(),
190
+ };
191
+ }
192
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.1", ngImport: i0, type: DataGridDeclarativeColumn, deps: [], target: i0.ɵɵFactoryTarget.Component });
193
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.2.0", version: "21.1.1", type: DataGridDeclarativeColumn, isStandalone: true, selector: "re-dg-column", inputs: { key: { classPropertyName: "key", publicName: "key", isSignal: true, isRequired: true, transformFunction: null }, header: { classPropertyName: "header", publicName: "header", isSignal: true, isRequired: false, transformFunction: null }, align: { classPropertyName: "align", publicName: "align", isSignal: true, isRequired: false, transformFunction: null }, sortKey: { classPropertyName: "sortKey", publicName: "sortKey", isSignal: true, isRequired: false, transformFunction: null }, sticky: { classPropertyName: "sticky", publicName: "sticky", isSignal: true, isRequired: false, transformFunction: null }, expandBy: { classPropertyName: "expandBy", publicName: "expandBy", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, visible: { classPropertyName: "visible", publicName: "visible", isSignal: true, isRequired: false, transformFunction: null }, width: { classPropertyName: "width", publicName: "width", isSignal: true, isRequired: false, transformFunction: null }, minWidth: { classPropertyName: "minWidth", publicName: "minWidth", isSignal: true, isRequired: false, transformFunction: null }, maxWidth: { classPropertyName: "maxWidth", publicName: "maxWidth", isSignal: true, isRequired: false, transformFunction: null }, flex: { classPropertyName: "flex", publicName: "flex", isSignal: true, isRequired: false, transformFunction: null }, type: { classPropertyName: "type", publicName: "type", isSignal: true, isRequired: false, transformFunction: null }, typeParams: { classPropertyName: "typeParams", publicName: "typeParams", isSignal: true, isRequired: false, transformFunction: null }, defaultValue: { classPropertyName: "defaultValue", publicName: "defaultValue", isSignal: true, isRequired: false, transformFunction: null }, value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: false, transformFunction: null }, track: { classPropertyName: "track", publicName: "track", isSignal: true, isRequired: false, transformFunction: null }, tooltip: { classPropertyName: "tooltip", publicName: "tooltip", isSignal: true, isRequired: false, transformFunction: null } }, queries: [{ propertyName: "headerTplRef", first: true, predicate: DataGridDeclarativeHeaderDirective, descendants: true, isSignal: true }, { propertyName: "cellTplRef", first: true, predicate: DataGridDeclarativeCellDirective, descendants: true, isSignal: true }], ngImport: i0, template: '<ng-content />', isInline: true });
194
+ }
195
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.1", ngImport: i0, type: DataGridDeclarativeColumn, decorators: [{
196
+ type: Component,
197
+ args: [{
198
+ selector: 're-dg-column',
199
+ template: '<ng-content />',
200
+ }]
201
+ }], propDecorators: { key: [{ type: i0.Input, args: [{ isSignal: true, alias: "key", required: true }] }], header: [{ type: i0.Input, args: [{ isSignal: true, alias: "header", required: false }] }], align: [{ type: i0.Input, args: [{ isSignal: true, alias: "align", required: false }] }], sortKey: [{ type: i0.Input, args: [{ isSignal: true, alias: "sortKey", required: false }] }], sticky: [{ type: i0.Input, args: [{ isSignal: true, alias: "sticky", required: false }] }], expandBy: [{ type: i0.Input, args: [{ isSignal: true, alias: "expandBy", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], visible: [{ type: i0.Input, args: [{ isSignal: true, alias: "visible", required: false }] }], width: [{ type: i0.Input, args: [{ isSignal: true, alias: "width", required: false }] }], minWidth: [{ type: i0.Input, args: [{ isSignal: true, alias: "minWidth", required: false }] }], maxWidth: [{ type: i0.Input, args: [{ isSignal: true, alias: "maxWidth", required: false }] }], flex: [{ type: i0.Input, args: [{ isSignal: true, alias: "flex", required: false }] }], type: [{ type: i0.Input, args: [{ isSignal: true, alias: "type", required: false }] }], typeParams: [{ type: i0.Input, args: [{ isSignal: true, alias: "typeParams", required: false }] }], defaultValue: [{ type: i0.Input, args: [{ isSignal: true, alias: "defaultValue", required: false }] }], value: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: false }] }], track: [{ type: i0.Input, args: [{ isSignal: true, alias: "track", required: false }] }], tooltip: [{ type: i0.Input, args: [{ isSignal: true, alias: "tooltip", required: false }] }], headerTplRef: [{ type: i0.ContentChild, args: [i0.forwardRef(() => DataGridDeclarativeHeaderDirective), { isSignal: true }] }], cellTplRef: [{ type: i0.ContentChild, args: [i0.forwardRef(() => DataGridDeclarativeCellDirective), { isSignal: true }] }] } });
202
+ function toOptionalNumber(value) {
203
+ if (value === null || value === undefined || value === '') {
204
+ return undefined;
205
+ }
206
+ const number = numberAttribute(value);
207
+ return Number.isNaN(number) ? undefined : number;
208
+ }
209
+
98
210
  /**
99
211
  * Directive for providing a custom template to display when the data grid has no data.
100
212
  *
@@ -146,6 +258,33 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.1", ngImpor
146
258
  args: [{ selector: 'ng-template[reDataGridLoading]' }]
147
259
  }] });
148
260
 
261
+ /**
262
+ * Directive for providing a custom template for sticky rows.
263
+ *
264
+ * Used as a structural directive on `<ng-template>` elements within data grid components.
265
+ * The template receives row data and its index.
266
+ *
267
+ * Example:
268
+ * ```html
269
+ * <ng-template reDataGridStickyRow let-row let-index="index">
270
+ * <div class="my-sticky-row">{{ index }} - {{ row.name }}</div>
271
+ * </ng-template>
272
+ * ```
273
+ *
274
+ * Template context:
275
+ * - `$implicit: Data` — row data
276
+ * - `index: number` — row index
277
+ */
278
+ class DataGridStickyRowDirective {
279
+ tpl = inject((TemplateRef));
280
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.1", ngImport: i0, type: DataGridStickyRowDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive });
281
+ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "21.1.1", type: DataGridStickyRowDirective, isStandalone: true, selector: "ng-template[reDataGridStickyRow]", ngImport: i0 });
282
+ }
283
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.1", ngImport: i0, type: DataGridStickyRowDirective, decorators: [{
284
+ type: Directive,
285
+ args: [{ selector: 'ng-template[reDataGridStickyRow]' }]
286
+ }] });
287
+
149
288
  class DataGridSortIconDirective {
150
289
  tpl = inject((TemplateRef));
151
290
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.1", ngImport: i0, type: DataGridSortIconDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive });
@@ -165,82 +304,56 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.1", ngImpor
165
304
  args: [{ selector: 'ng-template[reDataGridExpanderIcon]' }]
166
305
  }] });
167
306
 
168
- // noinspection CssUnresolvedCustomProperty
169
- class DataGridPaginator {
170
- current = input(0, { ...(ngDevMode ? { debugName: "current" } : {}), transform: numberAttribute });
171
- totalElements = input(0, { ...(ngDevMode ? { debugName: "totalElements" } : {}), transform: numberAttribute });
172
- pageSize = input(0, { ...(ngDevMode ? { debugName: "pageSize" } : {}), transform: numberAttribute });
173
- maxShowPages = input(7, { ...(ngDevMode ? { debugName: "maxShowPages" } : {}), transform: numberAttribute });
174
- pageChange = output();
175
- totalPages = computed(() => {
176
- const size = this.pageSize();
177
- return size > 0 ? Math.ceil(this.totalElements() / size) : 0;
178
- }, ...(ngDevMode ? [{ debugName: "totalPages" }] : []));
179
- pages = computed(() => {
180
- const current = this.current();
181
- const total = this.totalPages();
182
- const max = this.maxShowPages();
183
- if (total <= max) {
184
- return Array.from({ length: total }, (_, i) => i);
185
- }
186
- const pages = [];
187
- const sidePages = Math.floor((max - 3) / 2);
188
- pages.push(0);
189
- let start = Math.max(1, current - sidePages);
190
- let end = Math.min(total - 2, current + sidePages);
191
- if (current <= sidePages + 1) {
192
- end = max - 3;
193
- }
194
- else if (current >= total - sidePages - 2) {
195
- start = total - max + 2;
196
- }
197
- if (start > 1) {
198
- pages.push(-1);
199
- }
200
- for (let i = start; i <= end; i++) {
201
- pages.push(i);
202
- }
203
- if (end < total - 2) {
204
- pages.push(-1);
205
- }
206
- pages.push(total - 1);
207
- return pages;
208
- }, ...(ngDevMode ? [{ debugName: "pages" }] : []));
209
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.1", ngImport: i0, type: DataGridPaginator, deps: [], target: i0.ɵɵFactoryTarget.Component });
210
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.1.1", type: DataGridPaginator, isStandalone: true, selector: "re-data-grid-paginator", inputs: { current: { classPropertyName: "current", publicName: "current", isSignal: true, isRequired: false, transformFunction: null }, totalElements: { classPropertyName: "totalElements", publicName: "totalElements", isSignal: true, isRequired: false, transformFunction: null }, pageSize: { classPropertyName: "pageSize", publicName: "pageSize", isSignal: true, isRequired: false, transformFunction: null }, maxShowPages: { classPropertyName: "maxShowPages", publicName: "maxShowPages", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { pageChange: "pageChange" }, ngImport: i0, template: `
211
- @for (page of pages(); track $index) {
212
- @if (page === -1) {
213
- <span class="re-paginator-gap">...</span>
214
- } @else {
215
- <button
216
- class="re-paginator-page"
217
- [class.re-paginator-page--active]="page === current()"
218
- (click)="pageChange.emit(page)"
219
- >
220
- {{ page + 1 }}
221
- </button>
222
- }
223
- }
224
- `, isInline: true, styles: [":host{--re-data-grid-paginator-gap: .5rem;--re-data-grid-paginator-page-size: 1.75rem;--re-data-grid-paginator-page-border: 1px solid var(--re-data-grid-paginator-separator-color, #e2e8f0);--re-data-grid-paginator-page-separator-color: var(--re-data-grid-separator-color, --border-color);--re-data-grid-paginator-page-rounded: var(--re-data-grid-rounded, --radius-md);--re-data-grid-paginator-page-surface: var(--re-data-grid-surface, white);--re-data-grid-paginator-page-color: var(--text-primary, #1e293b);--re-data-grid-paginator-page-font-size: .875rem;--re-data-grid-paginator-page-active-surface: var(--re-data-grid-active, #3b82f6);--re-data-grid-paginator-page-active-color: white;--re-data-grid-paginator-page-hover-surface: var(--re-data-grid-active, #3b82f6);--re-data-grid-paginator-page-hover-color: white;display:flex;align-items:center;gap:var(--re-data-grid-paginator-gap)}.re-paginator-page{display:flex;justify-content:center;align-items:center;width:var(--re-data-grid-paginator-page-size);height:var(--re-data-grid-paginator-page-size);border-radius:var(--re-data-grid-paginator-page-rounded);border:var(--re-data-grid-paginator-page-border);font-size:var(--re-data-grid-paginator-page-font-size);background:var(--re-data-grid-paginator-page-surface);color:var(--re-data-grid-paginator-page-color);cursor:pointer;transition:all .2s}.re-paginator-page:hover:not(.re-paginator-page--active){background:var(--re-data-grid-paginator-page-hover-surface);color:var(--re-data-grid-paginator-page-hover-color)}.re-paginator-page--active{border-color:var(--re-data-grid-paginator-active-surface);background:var(--re-data-grid-paginator-page-active-surface);color:var(--re-data-grid-paginator-page-active-color)}.re-paginator-gap{display:flex;align-items:center;justify-content:center;padding:var(--re-data-grid-paginator-page-paddings);color:var(--re-data-grid-paginator-page-color)}\n"] });
307
+ const DEFAULT_DATA_GRID_DEFAULTS = {
308
+ mode: 'pagination',
309
+ hasIndexColumn: false,
310
+ selection: { mode: 'none' },
311
+ pageSize: 20,
312
+ rowHeight: 40,
313
+ headerHeight: 48,
314
+ height: 'default',
315
+ virtualBuffer: 8,
316
+ loadingMode: 'spinner',
317
+ pageStartFromZero: true,
318
+ translations: {
319
+ emptyState: 'No records found',
320
+ itemsPerPageLabel: 'Items per page:',
321
+ nextPageLabel: 'Next page',
322
+ prevPageLabel: 'Previous page',
323
+ },
324
+ debounce: {
325
+ resize: 100,
326
+ scroll: 50,
327
+ },
328
+ };
329
+ const DATA_GRID_CONFIG = new InjectionToken('RE_DATA_GRID_DEFAULTS', {
330
+ providedIn: 'root',
331
+ factory: () => DEFAULT_DATA_GRID_DEFAULTS,
332
+ });
333
+ /**
334
+ * Provides default configuration for Data Grid, overriding base settings.
335
+ *
336
+ * @param config Partial configuration object to be merged with current settings.
337
+ * @returns EnvironmentProviders for use in application or component configuration.
338
+ */
339
+ function provideDataGridDefaults(config) {
340
+ return makeEnvironmentProviders([
341
+ {
342
+ provide: DATA_GRID_CONFIG,
343
+ useFactory: () => {
344
+ const parent = inject(DATA_GRID_CONFIG, { optional: true, skipSelf: true });
345
+ const base = parent ?? DEFAULT_DATA_GRID_DEFAULTS;
346
+ return {
347
+ ...base,
348
+ ...config,
349
+ selection: config.selection ?? base.selection,
350
+ translations: { ...base.translations, ...config.translations },
351
+ debounce: { ...base.debounce, ...config.debounce },
352
+ };
353
+ },
354
+ },
355
+ ]);
225
356
  }
226
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.1", ngImport: i0, type: DataGridPaginator, decorators: [{
227
- type: Component,
228
- args: [{ selector: 're-data-grid-paginator', template: `
229
- @for (page of pages(); track $index) {
230
- @if (page === -1) {
231
- <span class="re-paginator-gap">...</span>
232
- } @else {
233
- <button
234
- class="re-paginator-page"
235
- [class.re-paginator-page--active]="page === current()"
236
- (click)="pageChange.emit(page)"
237
- >
238
- {{ page + 1 }}
239
- </button>
240
- }
241
- }
242
- `, styles: [":host{--re-data-grid-paginator-gap: .5rem;--re-data-grid-paginator-page-size: 1.75rem;--re-data-grid-paginator-page-border: 1px solid var(--re-data-grid-paginator-separator-color, #e2e8f0);--re-data-grid-paginator-page-separator-color: var(--re-data-grid-separator-color, --border-color);--re-data-grid-paginator-page-rounded: var(--re-data-grid-rounded, --radius-md);--re-data-grid-paginator-page-surface: var(--re-data-grid-surface, white);--re-data-grid-paginator-page-color: var(--text-primary, #1e293b);--re-data-grid-paginator-page-font-size: .875rem;--re-data-grid-paginator-page-active-surface: var(--re-data-grid-active, #3b82f6);--re-data-grid-paginator-page-active-color: white;--re-data-grid-paginator-page-hover-surface: var(--re-data-grid-active, #3b82f6);--re-data-grid-paginator-page-hover-color: white;display:flex;align-items:center;gap:var(--re-data-grid-paginator-gap)}.re-paginator-page{display:flex;justify-content:center;align-items:center;width:var(--re-data-grid-paginator-page-size);height:var(--re-data-grid-paginator-page-size);border-radius:var(--re-data-grid-paginator-page-rounded);border:var(--re-data-grid-paginator-page-border);font-size:var(--re-data-grid-paginator-page-font-size);background:var(--re-data-grid-paginator-page-surface);color:var(--re-data-grid-paginator-page-color);cursor:pointer;transition:all .2s}.re-paginator-page:hover:not(.re-paginator-page--active){background:var(--re-data-grid-paginator-page-hover-surface);color:var(--re-data-grid-paginator-page-hover-color)}.re-paginator-page--active{border-color:var(--re-data-grid-paginator-active-surface);background:var(--re-data-grid-paginator-page-active-surface);color:var(--re-data-grid-paginator-page-active-color)}.re-paginator-gap{display:flex;align-items:center;justify-content:center;padding:var(--re-data-grid-paginator-page-paddings);color:var(--re-data-grid-paginator-page-color)}\n"] }]
243
- }], propDecorators: { current: [{ type: i0.Input, args: [{ isSignal: true, alias: "current", required: false }] }], totalElements: [{ type: i0.Input, args: [{ isSignal: true, alias: "totalElements", required: false }] }], pageSize: [{ type: i0.Input, args: [{ isSignal: true, alias: "pageSize", required: false }] }], maxShowPages: [{ type: i0.Input, args: [{ isSignal: true, alias: "maxShowPages", required: false }] }], pageChange: [{ type: i0.Output, args: ["pageChange"] }] } });
244
357
 
245
358
  const GRID_INDEX_COLUMN = {
246
359
  key: '_index',
@@ -248,7 +361,7 @@ const GRID_INDEX_COLUMN = {
248
361
  align: 'center',
249
362
  header: '№',
250
363
  width: 72,
251
- sticky: true,
364
+ sticky: 'left',
252
365
  };
253
366
  const GRID_CHECKBOX_COLUMN = {
254
367
  key: '_checkbox',
@@ -256,7 +369,7 @@ const GRID_CHECKBOX_COLUMN = {
256
369
  align: 'center',
257
370
  header: 'checkbox',
258
371
  width: 44,
259
- sticky: true,
372
+ sticky: 'left',
260
373
  };
261
374
 
262
375
  /**
@@ -492,7 +605,7 @@ function roundAndFix(cols, widths, fixedIdx, autoIdx, containerWidth) {
492
605
  *
493
606
  * If `min` is provided and the value is less than it, returns `min`.
494
607
  * If `max` is provided and the value is greater than it, returns `max`.
495
- * Otherwise returns the original value.
608
+ * Otherwise, returns the original value.
496
609
  *
497
610
  * @param v - Value to clamp
498
611
  * @param min - Optional minimum bound
@@ -521,6 +634,116 @@ function sum(items) {
521
634
  return items.reduce((s, x) => s + x.width, 0);
522
635
  }
523
636
 
637
+ // noinspection ES6PreferShortImport
638
+ const DEFAULT_COLUMN_WIDTH = 140;
639
+ /**
640
+ * Computes and normalizes header groups based on the provided headers and columns.
641
+ *
642
+ * @param {GridHeaderGroup<Data>[]} headers - An array of header group objects representing the grouping information
643
+ * for the grid.
644
+ * @param {GridColumn<Data>[]} columns - An array of column definitions describing the columns in the grid.
645
+ * @return {NormalizedHeader[]} An array of normalized header objects, where each object contains information about
646
+ * the computed header ranges, widths, and keys.
647
+ */
648
+ function computeHeaderGroups(headers = [], columns = []) {
649
+ if (!columns.length || !headers.length) {
650
+ return [];
651
+ }
652
+ const keyToIndex = new Map(columns.map((col, index) => [col.key, index]));
653
+ const columnWidths = columns.map(resolveColumnWidth);
654
+ const prefix = buildWidthesPrefixSums(columnWidths);
655
+ const ranges = [];
656
+ const occupied = new Set();
657
+ for (const header of headers) {
658
+ const start = keyToIndex.get(header.from);
659
+ const resolvedTo = header.to ?? header.from;
660
+ const end = keyToIndex.get(resolvedTo);
661
+ if (start === undefined || end === undefined) {
662
+ continue;
663
+ }
664
+ const rangeStart = Math.min(start, end);
665
+ const rangeEnd = Math.max(start, end);
666
+ if (hasOverlap(rangeStart, rangeEnd, occupied)) {
667
+ continue;
668
+ }
669
+ for (let i = rangeStart; i <= rangeEnd; i++) {
670
+ occupied.add(i);
671
+ }
672
+ ranges.push({ header, start: rangeStart, end: rangeEnd });
673
+ }
674
+ const rangesByStart = new Map(ranges.map((range) => [range.start, range]));
675
+ const normalized = [];
676
+ for (let index = 0; index < columns.length;) {
677
+ const range = rangesByStart.get(index);
678
+ if (range) {
679
+ normalized.push(normalizeRange(range, prefix, columns));
680
+ index = range.end + 1;
681
+ continue;
682
+ }
683
+ const start = index;
684
+ while (index < columns.length && !rangesByStart.has(index)) {
685
+ index++;
686
+ }
687
+ const end = index - 1;
688
+ normalized.push({
689
+ key: `_rest_${columns[start].key}_${columns[end].key}`,
690
+ widthPx: sumByRange(prefix, start, end),
691
+ startKey: columns[start]?.key,
692
+ endKey: columns[end]?.key,
693
+ });
694
+ }
695
+ return normalized;
696
+ }
697
+ function normalizeRange(range, prefix, columns) {
698
+ const base = {
699
+ key: range.header.key,
700
+ widthPx: sumByRange(prefix, range.start, range.end),
701
+ startKey: columns[range.start]?.key,
702
+ endKey: columns[range.end]?.key,
703
+ };
704
+ if ('titleTemplate' in range.header) {
705
+ base.titleTemplate = range.header.titleTemplate;
706
+ return base;
707
+ }
708
+ base.title = range.header.title;
709
+ base.align = range.header.align;
710
+ return base;
711
+ }
712
+ function resolveColumnWidth(column) {
713
+ const width = asFiniteNumber(column.width);
714
+ const minWidth = asFiniteNumber(column.minWidth);
715
+ const maxWidth = asFiniteNumber(column.maxWidth);
716
+ let result = width ?? minWidth ?? DEFAULT_COLUMN_WIDTH;
717
+ if (minWidth !== null && result < minWidth) {
718
+ result = minWidth;
719
+ }
720
+ if (maxWidth !== null && result > maxWidth) {
721
+ result = maxWidth;
722
+ }
723
+ return Math.max(0, Math.round(result));
724
+ }
725
+ function asFiniteNumber(value) {
726
+ return typeof value === 'number' && Number.isFinite(value) ? value : null;
727
+ }
728
+ function hasOverlap(start, end, occupied) {
729
+ for (let i = start; i <= end; i++) {
730
+ if (occupied.has(i)) {
731
+ return true;
732
+ }
733
+ }
734
+ return false;
735
+ }
736
+ function buildWidthesPrefixSums(values) {
737
+ const prefix = new Array(values.length + 1).fill(0);
738
+ for (let i = 0; i < values.length; i++) {
739
+ prefix[i + 1] = prefix[i] + values[i];
740
+ }
741
+ return prefix;
742
+ }
743
+ function sumByRange(prefix, start, end) {
744
+ return prefix[end + 1] - prefix[start];
745
+ }
746
+
524
747
  /**
525
748
  * Calculates the visual state of an overlay scrollbar. All arguments are raw DOM numbers.
526
749
  * Returns a state describing the visibility, size, and position of the thumb.
@@ -558,7 +781,7 @@ function mapThumbTopToScrollTop(thumbTop, maxThumbTop, maxScrollTop) {
558
781
  const tt = clamp(toSafe(thumbTop), 0, mt);
559
782
  return (tt / mt) * ms;
560
783
  }
561
- /** Clamps the new thumb position within the bounds 0..maxThumbTop */
784
+ /** Clamps the new thumb position within bounds 0..maxThumbTop */
562
785
  function clampThumbTop(value, maxThumbTop) {
563
786
  const mt = Math.max(1, toSafe(maxThumbTop));
564
787
  return clamp(toSafe(value), 0, mt);
@@ -677,9 +900,8 @@ class ScrollDirectionDetector {
677
900
  */
678
901
  function splitSticky(cols) {
679
902
  const visible = cols.filter((c) => c.visible !== false);
680
- const middleIndex = Math.floor(visible.length / 2);
681
- const left = visible.slice(0, middleIndex).filter((col) => col.sticky);
682
- const right = visible.slice(middleIndex, visible.length).filter((col) => col.sticky);
903
+ const left = visible.filter((col) => col.sticky === 'left' || col.sticky === true);
904
+ const right = visible.filter((col) => col.sticky === 'right');
683
905
  return { left, right, visible };
684
906
  }
685
907
 
@@ -720,6 +942,7 @@ class DataGridVm {
720
942
  * Each row includes data, position ('top' or 'bottom'), and optional ordering.
721
943
  */
722
944
  pinnedRows = signal([], ...(ngDevMode ? [{ debugName: "pinnedRows" }] : []));
945
+ headerGroups = signal([], ...(ngDevMode ? [{ debugName: "headerGroups" }] : []));
723
946
  /**
724
947
  * Current width of the grid container in pixels.
725
948
  *
@@ -759,10 +982,14 @@ class DataGridVm {
759
982
  */
760
983
  globalTypeCellTpls = new Map();
761
984
  globalDataCellTpls = new Map();
985
+ globalRowCellTpls = new Map();
762
986
  #byKey = signal({}, ...(ngDevMode ? [{ debugName: "#byKey" }] : []));
763
987
  #defaultColWidth = 140;
764
988
  #stickyLeftMap = new Map();
765
989
  #stickyRightMap = new Map();
990
+ #stickySplit = computed(() => splitSticky(this.columns() ?? []), ...(ngDevMode ? [{ debugName: "#stickySplit" }] : []));
991
+ pinnedTop = signal([], ...(ngDevMode ? [{ debugName: "pinnedTop" }] : []));
992
+ pinnedBottom = signal([], ...(ngDevMode ? [{ debugName: "pinnedBottom" }] : []));
766
993
  /**
767
994
  * Computed an array of non-sticky columns to display in the scrollable area.
768
995
  *
@@ -772,26 +999,43 @@ class DataGridVm {
772
999
  */
773
1000
  columnsToShow = computed(() => {
774
1001
  this.containerWidth();
775
- const cols = this.columns() ?? [];
776
- const { left, visible, right } = splitSticky(cols);
777
- this.recomputeStickyOffsets(left, right);
778
- return visible;
1002
+ return this.#stickySplit().visible;
779
1003
  }, ...(ngDevMode ? [{ debugName: "columnsToShow" }] : []));
1004
+ contentWidth = computed(() => {
1005
+ const columns = this.columnsToShow();
1006
+ if (!columns.length) {
1007
+ return 0;
1008
+ }
1009
+ return columns.reduce((sum, col) => sum + this.widthByKey(col.key), 0);
1010
+ }, ...(ngDevMode ? [{ debugName: "contentWidth" }] : []));
780
1011
  /**
781
- * Computed array of rows pinned to the top of the grid.
1012
+ * Computed array of normalized header groups with calculated widths.
782
1013
  *
783
- * Filters pinned rows by 'top' position and sorts them by order property.
784
- * Rows with lower order values appear first.
785
- */
786
- pinnedTop = computed(() => (this.pinnedRows() ?? []).filter((r) => r.position === 'top').sort((a, b) => (a.order ?? 0) - (b.order ?? 0)), ...(ngDevMode ? [{ debugName: "pinnedTop" }] : []));
787
- /**
788
- * Computed array of rows pinned to the bottom of the grid.
1014
+ * Transforms raw header group configurations into normalized structures
1015
+ * that include computed column widths for layout rendering. Returns an empty
1016
+ * array if no headers or columns are configured. Recalculates whenever
1017
+ * header groups or visible columns change.
789
1018
  *
790
- * Filters pinned rows by 'bottom' position and sorts them by order property.
791
- * Rows with lower order values appear first.
1019
+ * @returns Array of normalized header configurations with width calculations
792
1020
  */
793
- pinnedBottom = computed(() => (this.pinnedRows() ?? []).filter((r) => r.position === 'bottom').sort((a, b) => (a.order ?? 0) - (b.order ?? 0)), ...(ngDevMode ? [{ debugName: "pinnedBottom" }] : []));
1021
+ normalizedHeaderGroups = computed(() => {
1022
+ const headers = this.headerGroups();
1023
+ const columns = this.columnsToShow();
1024
+ if (!headers.length || !columns.length) {
1025
+ return [];
1026
+ }
1027
+ const normalizedColumns = columns.map((col) => ({
1028
+ ...col,
1029
+ width: this.widthByKey(col.key),
1030
+ }));
1031
+ return computeHeaderGroups(headers, normalizedColumns);
1032
+ }, ...(ngDevMode ? [{ debugName: "normalizedHeaderGroups" }] : []));
794
1033
  constructor() {
1034
+ effect(() => {
1035
+ const rows = this.pinnedRows() ?? [];
1036
+ this.pinnedTop.set(rows.filter((r) => r.position === 'top').sort((a, b) => (a.order ?? 0) - (b.order ?? 0)));
1037
+ this.pinnedBottom.set(rows.filter((r) => r.position === 'bottom').sort((a, b) => (a.order ?? 0) - (b.order ?? 0)));
1038
+ });
795
1039
  effect(() => {
796
1040
  const cols = this.columns();
797
1041
  const width = this.containerWidth();
@@ -801,6 +1045,11 @@ class DataGridVm {
801
1045
  const res = layoutColumns(cols, width, this.#defaultColWidth);
802
1046
  this.#byKey.set(res.byKey);
803
1047
  });
1048
+ effect(() => {
1049
+ this.#byKey();
1050
+ const { left, right } = this.#stickySplit();
1051
+ this.recomputeStickyOffsets(left, right);
1052
+ });
804
1053
  }
805
1054
  /**
806
1055
  * Returns the computed width for a column by its key.
@@ -883,6 +1132,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.1", ngImpor
883
1132
  type: Injectable
884
1133
  }], ctorParameters: () => [] });
885
1134
 
1135
+ // noinspection ES6PreferShortImport
886
1136
  /**
887
1137
  * Component for rendering individual data grid cells.
888
1138
  *
@@ -919,7 +1169,7 @@ class DataGridCellComponent {
919
1169
  * Data object for the current row.
920
1170
  *
921
1171
  * Contains the complete row data that can be accessed by column value functions
922
- * or custom render templates. Type-safe according to the Data generic parameter.
1172
+ * or custom render templates. Type-safe, according to the Data generic parameter.
923
1173
  */
924
1174
  item = input.required(...(ngDevMode ? [{ debugName: "item" }] : []));
925
1175
  /**
@@ -933,7 +1183,7 @@ class DataGridCellComponent {
933
1183
  * Injected DataGridVm service instance.
934
1184
  *
935
1185
  * Provides access to global type-specific cell templates registered
936
- * at the grid level via `globalTypeCellTpls` map.
1186
+ * at the grid level via the ` globalTypeCellTpls ` map.
937
1187
  */
938
1188
  vm = inject(DataGridVm);
939
1189
  /**
@@ -945,7 +1195,7 @@ class DataGridCellComponent {
945
1195
  * - `'date'`, `'number'`, `'index'` - Built-in formatters
946
1196
  * - `'plain'` - Default text rendering
947
1197
  *
948
- * The value is reactive and updates when column configuration changes.
1198
+ * The value is reactive and updates when the column configuration changes.
949
1199
  */
950
1200
  type = computed(() => {
951
1201
  const col = this.column();
@@ -958,6 +1208,9 @@ class DataGridCellComponent {
958
1208
  if (this.vm.globalDataCellTpls.has(col.key)) {
959
1209
  return 'globalDataTpl';
960
1210
  }
1211
+ if (this.vm.globalRowCellTpls.has(col.key)) {
1212
+ return 'globalRowTpl';
1213
+ }
961
1214
  return 'type' in col ? col['type'] : 'plain';
962
1215
  }, ...(ngDevMode ? [{ debugName: "type" }] : []));
963
1216
  /**
@@ -973,38 +1226,51 @@ class DataGridCellComponent {
973
1226
  value = computed(() => {
974
1227
  const col = this.column();
975
1228
  const row = this.item();
976
- return 'value' in col ? col.value(row) : row[col.key];
1229
+ const rawValue = 'value' in col ? col.value(row) : row[col.key];
1230
+ if ('value' in col || 'renderTemplate' in col) {
1231
+ return rawValue;
1232
+ }
1233
+ if (rawValue === null || rawValue === undefined) {
1234
+ return col.defaultValue ?? rawValue;
1235
+ }
1236
+ return rawValue;
977
1237
  }, ...(ngDevMode ? [{ debugName: "value" }] : []));
978
1238
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.1", ngImport: i0, type: DataGridCellComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
979
1239
  static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.1.1", type: DataGridCellComponent, isStandalone: true, selector: "re-data-grid-cell", inputs: { index: { classPropertyName: "index", publicName: "index", isSignal: true, isRequired: true, transformFunction: null }, item: { classPropertyName: "item", publicName: "item", isSignal: true, isRequired: true, transformFunction: null }, column: { classPropertyName: "column", publicName: "column", isSignal: true, isRequired: true, transformFunction: null } }, ngImport: i0, template: `
980
1240
  @let row = item();
981
- @let col = column();
1241
+ @let col = $any(column());
982
1242
  @let val = value();
983
1243
 
984
1244
  @switch (type()) {
985
1245
  @case ('tpl') {
986
1246
  <ng-container
987
- [ngTemplateOutlet]="$any(col).renderTemplate"
1247
+ [ngTemplateOutlet]="col.renderTemplate"
988
1248
  [ngTemplateOutletContext]="{ $implicit: val, value: val, row, col, index: index() }"
989
1249
  />
990
1250
  }
991
1251
  @case ('globalTypeTpl') {
992
1252
  <ng-container
993
- [ngTemplateOutlet]="vm.globalTypeCellTpls.get($any(col).type)"
1253
+ [ngTemplateOutlet]="vm.globalTypeCellTpls.get(col.type)"
994
1254
  [ngTemplateOutletContext]="{ $implicit: val, value: val, row, col }"
995
1255
  />
996
1256
  }
997
1257
  @case ('globalDataTpl') {
998
1258
  <ng-container
999
- [ngTemplateOutlet]="vm.globalDataCellTpls.get($any(col).key)"
1259
+ [ngTemplateOutlet]="vm.globalDataCellTpls.get(col.key)"
1000
1260
  [ngTemplateOutletContext]="{ $implicit: val, value: val, row, col }"
1001
1261
  />
1002
1262
  }
1263
+ @case ('globalRowTpl') {
1264
+ <ng-container
1265
+ [ngTemplateOutlet]="vm.globalRowCellTpls.get(col.key)"
1266
+ [ngTemplateOutletContext]="{ $implicit: val, value: val, row, col, index: index() }"
1267
+ />
1268
+ }
1003
1269
  @case ('date') {
1004
- {{ val | date: $any(col)?.typeParams }}
1270
+ {{ val | date: col?.typeParams }}
1005
1271
  }
1006
1272
  @case ('number') {
1007
- {{ val | number: $any(col)?.typeParams }}
1273
+ {{ val | number: col?.typeParams }}
1008
1274
  }
1009
1275
  @case ('index') {
1010
1276
  {{ index() + 1 }}
@@ -1019,33 +1285,39 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.1", ngImpor
1019
1285
  type: Component,
1020
1286
  args: [{ selector: 're-data-grid-cell', template: `
1021
1287
  @let row = item();
1022
- @let col = column();
1288
+ @let col = $any(column());
1023
1289
  @let val = value();
1024
1290
 
1025
1291
  @switch (type()) {
1026
1292
  @case ('tpl') {
1027
1293
  <ng-container
1028
- [ngTemplateOutlet]="$any(col).renderTemplate"
1294
+ [ngTemplateOutlet]="col.renderTemplate"
1029
1295
  [ngTemplateOutletContext]="{ $implicit: val, value: val, row, col, index: index() }"
1030
1296
  />
1031
1297
  }
1032
1298
  @case ('globalTypeTpl') {
1033
1299
  <ng-container
1034
- [ngTemplateOutlet]="vm.globalTypeCellTpls.get($any(col).type)"
1300
+ [ngTemplateOutlet]="vm.globalTypeCellTpls.get(col.type)"
1035
1301
  [ngTemplateOutletContext]="{ $implicit: val, value: val, row, col }"
1036
1302
  />
1037
1303
  }
1038
1304
  @case ('globalDataTpl') {
1039
1305
  <ng-container
1040
- [ngTemplateOutlet]="vm.globalDataCellTpls.get($any(col).key)"
1306
+ [ngTemplateOutlet]="vm.globalDataCellTpls.get(col.key)"
1041
1307
  [ngTemplateOutletContext]="{ $implicit: val, value: val, row, col }"
1042
1308
  />
1043
1309
  }
1310
+ @case ('globalRowTpl') {
1311
+ <ng-container
1312
+ [ngTemplateOutlet]="vm.globalRowCellTpls.get(col.key)"
1313
+ [ngTemplateOutletContext]="{ $implicit: val, value: val, row, col, index: index() }"
1314
+ />
1315
+ }
1044
1316
  @case ('date') {
1045
- {{ val | date: $any(col)?.typeParams }}
1317
+ {{ val | date: col?.typeParams }}
1046
1318
  }
1047
1319
  @case ('number') {
1048
- {{ val | number: $any(col)?.typeParams }}
1320
+ {{ val | number: col?.typeParams }}
1049
1321
  }
1050
1322
  @case ('index') {
1051
1323
  {{ index() + 1 }}
@@ -1057,6 +1329,60 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.1", ngImpor
1057
1329
  `, imports: [NgTemplateOutlet, DatePipe, DecimalPipe], styles: [":host{width:100%;text-align:inherit}\n"] }]
1058
1330
  }], propDecorators: { index: [{ type: i0.Input, args: [{ isSignal: true, alias: "index", required: true }] }], item: [{ type: i0.Input, args: [{ isSignal: true, alias: "item", required: true }] }], column: [{ type: i0.Input, args: [{ isSignal: true, alias: "column", required: true }] }] } });
1059
1331
 
1332
+ // noinspection ES6PreferShortImport
1333
+ function normalizeDeclarativeColumns(defs = [], rowKey) {
1334
+ const columns = [];
1335
+ const rowCellTemplatesByKey = new Map();
1336
+ for (const def of defs) {
1337
+ const key = def.key;
1338
+ const base = {
1339
+ key,
1340
+ header: def.header ?? String(key),
1341
+ headerTemplate: def.headerTemplate,
1342
+ align: def.align,
1343
+ sortKey: def.sortKey,
1344
+ width: def.width,
1345
+ minWidth: def.minWidth,
1346
+ maxWidth: def.maxWidth,
1347
+ flex: def.flex,
1348
+ disabled: def.disabled,
1349
+ visible: def.visible,
1350
+ sticky: def.sticky,
1351
+ tooltip: def.tooltip,
1352
+ expandBy: def.expandBy,
1353
+ };
1354
+ if (def.cellTemplate) {
1355
+ rowCellTemplatesByKey.set(String(key), def.cellTemplate);
1356
+ }
1357
+ if (def.value) {
1358
+ columns.push({
1359
+ ...base,
1360
+ value: def.value,
1361
+ track: def.track ?? createDefaultTrack(key, rowKey),
1362
+ });
1363
+ continue;
1364
+ }
1365
+ columns.push({
1366
+ ...base,
1367
+ type: def.type ?? 'plain',
1368
+ typeParams: def.typeParams,
1369
+ defaultValue: def.defaultValue,
1370
+ });
1371
+ }
1372
+ return { columns, rowCellTemplatesByKey };
1373
+ }
1374
+ function createDefaultTrack(key, rowKey) {
1375
+ return (row) => {
1376
+ if (!rowKey) {
1377
+ return String(row[key]);
1378
+ }
1379
+ if (typeof rowKey === 'function') {
1380
+ return String(rowKey(row));
1381
+ }
1382
+ return String(row[rowKey]);
1383
+ };
1384
+ }
1385
+
1060
1386
  /**
1061
1387
  * Service class for managing row selection in a data grid.
1062
1388
  *
@@ -1106,7 +1432,7 @@ class Selector {
1106
1432
  * - `false` if no rows are selected
1107
1433
  * - `'mixed'` if some but not all rows are selected
1108
1434
  *
1109
- * Useful for implementing "select all" checkbox with indeterminate state.
1435
+ * Useful for implementing the "select all" checkbox with an indeterminate state.
1110
1436
  */
1111
1437
  isAllSelected = computed(() => {
1112
1438
  const selectedCount = this.selectedKeys().length;
@@ -1116,7 +1442,7 @@ class Selector {
1116
1442
  * Checks whether a specific row is currently selected.
1117
1443
  *
1118
1444
  * Compares the row's key value against the list of selected keys.
1119
- * Returns `false` if selection mode is 'none' or key is not configured.
1445
+ * Returns `false` if the selection mode is 'none' or the key is not configured.
1120
1446
  *
1121
1447
  * @param row - The data row to check selection status for.
1122
1448
  * @returns `true` if the row is selected, `false` otherwise.
@@ -1337,6 +1663,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.1", ngImpor
1337
1663
  * ```
1338
1664
  */
1339
1665
  class DataGrid {
1666
+ defaults = inject(DATA_GRID_CONFIG) || DEFAULT_DATA_GRID_DEFAULTS;
1340
1667
  /**
1341
1668
  * Array of data to display in the table.
1342
1669
  *
@@ -1358,7 +1685,7 @@ class DataGrid {
1358
1685
  * - `pagination` - Classic page-based pagination with fixed page size
1359
1686
  * - `infinity` - Infinite scroll mode, loads more data as user scrolls
1360
1687
  */
1361
- mode = input('none', ...(ngDevMode ? [{ debugName: "mode" }] : []));
1688
+ mode = input(this.defaults.mode, ...(ngDevMode ? [{ debugName: "mode" }] : []));
1362
1689
  /**
1363
1690
  * Array of pinned rows that remain visible at the top or bottom.
1364
1691
  *
@@ -1366,12 +1693,26 @@ class DataGrid {
1366
1693
  * Useful for totals, summaries, or always-visible items.
1367
1694
  */
1368
1695
  pinnedRows = input([], ...(ngDevMode ? [{ debugName: "pinnedRows" }] : []));
1696
+ /**
1697
+ * Function to determine if a row should become sticky at the top.
1698
+ *
1699
+ * When provided, rows matching this predicate will stick to the top
1700
+ * of the scroll area as the user scrolls.
1701
+ */
1702
+ isRowSticky = input(undefined, ...(ngDevMode ? [{ debugName: "isRowSticky" }] : []));
1703
+ /**
1704
+ * Function to choose a custom template for a row.
1705
+ *
1706
+ * If it returns a template, the row will be rendered with it;
1707
+ * otherwise, the default row rendering is used.
1708
+ */
1709
+ getRowTemplate = input(undefined, ...(ngDevMode ? [{ debugName: "getRowTemplate" }] : []));
1369
1710
  /**
1370
1711
  * Whether to add an index column showing row numbers.
1371
1712
  *
1372
1713
  * When enabled, it automatically adds a column displaying sequential row numbers.
1373
1714
  */
1374
- hasIndexColumn = input(false, { ...(ngDevMode ? { debugName: "hasIndexColumn" } : {}), transform: booleanAttribute });
1715
+ hasIndexColumn = input(this.defaults.hasIndexColumn, { ...(ngDevMode ? { debugName: "hasIndexColumn" } : {}), transform: booleanAttribute });
1375
1716
  /**
1376
1717
  * Row selection configuration.
1377
1718
  *
@@ -1382,21 +1723,21 @@ class DataGrid {
1382
1723
  *
1383
1724
  * When selection is enabled, a `key` must be provided to identify rows.
1384
1725
  */
1385
- selection = input({ mode: 'none' }, ...(ngDevMode ? [{ debugName: "selection" }] : []));
1726
+ selection = input(this.defaults.selection, ...(ngDevMode ? [{ debugName: "selection" }] : []));
1386
1727
  /**
1387
1728
  * Number of items per page.
1388
1729
  *
1389
1730
  * Used in pagination and infinity scroll modes to control
1390
1731
  * how many rows are loaded at once. Default is 20.
1391
1732
  */
1392
- pageSize = input(20, { ...(ngDevMode ? { debugName: "pageSize" } : {}), transform: numberAttribute });
1733
+ pageSize = input(this.defaults.pageSize, { ...(ngDevMode ? { debugName: "pageSize" } : {}), transform: numberAttribute });
1393
1734
  /**
1394
1735
  * Height of each row in pixels.
1395
1736
  *
1396
1737
  * Used for virtual scrolling calculations. Must be consistent
1397
1738
  * across all rows for accurate scrolling. Default is 40.
1398
1739
  */
1399
- rowHeight = input(40, { ...(ngDevMode ? { debugName: "rowHeight" } : {}), transform: numberAttribute });
1740
+ rowHeight = input(this.defaults.rowHeight, { ...(ngDevMode ? { debugName: "rowHeight" } : {}), transform: numberAttribute });
1400
1741
  /**
1401
1742
  * Grid height configuration.
1402
1743
  *
@@ -1404,14 +1745,34 @@ class DataGrid {
1404
1745
  * - `'full'` - Fill container height (100%), can be managed by host component style
1405
1746
  * - `'default'` - Height by CSS var (--re-data-grid-height)
1406
1747
  */
1407
- height = input('default', ...(ngDevMode ? [{ debugName: "height" }] : []));
1748
+ height = input(this.defaults.height, ...(ngDevMode ? [{ debugName: "height" }] : []));
1408
1749
  /**
1409
1750
  * Size of the virtual scroll buffer.
1410
1751
  *
1411
1752
  * Number of extra rows to render above and below the viewport
1412
- * to reduce flickering during fast scrolling. Default is 6.
1753
+ * to reduce flickering during fast scrolling. Default is 8.
1754
+ */
1755
+ virtualBuffer = input(this.defaults.virtualBuffer, ...(ngDevMode ? [{ debugName: "virtualBuffer" }] : []));
1756
+ /**
1757
+ * Header group configuration for creating multi-level column headers.
1758
+ *
1759
+ * Allows grouping multiple columns under a common header label.
1760
+ * Each group spans from a starting column to an ending column and can have a custom alignment.
1761
+ *
1762
+ * @example
1763
+ * ```typescript
1764
+ * headerGroups = [
1765
+ * {
1766
+ * key: 'personal-info',
1767
+ * title: 'Personal Information',
1768
+ * from: 'name',
1769
+ * to: 'email',
1770
+ * align: 'center'
1771
+ * }
1772
+ * ];
1773
+ * ```
1413
1774
  */
1414
- virtualBuffer = input(6, ...(ngDevMode ? [{ debugName: "virtualBuffer" }] : []));
1775
+ headerGroups = input([], ...(ngDevMode ? [{ debugName: "headerGroups" }] : []));
1415
1776
  /**
1416
1777
  * Loading state indicator.
1417
1778
  *
@@ -1419,8 +1780,9 @@ class DataGrid {
1419
1780
  * Useful during async data fetching operations.
1420
1781
  */
1421
1782
  loading = input(false, { ...(ngDevMode ? { debugName: "loading" } : {}), transform: booleanAttribute });
1783
+ loadingMode = input(this.defaults.loadingMode, ...(ngDevMode ? [{ debugName: "loadingMode" }] : []));
1422
1784
  /**
1423
- * Function or property name for obtaining a unique row key.
1785
+ * Function or property name for getting a unique row key.
1424
1786
  *
1425
1787
  * Used for efficient change detection and row tracking.
1426
1788
  * Can be a property name (string) or a function that returns a unique identifier.
@@ -1432,7 +1794,7 @@ class DataGrid {
1432
1794
  * Controls the numbering scheme for pagination events.
1433
1795
  * Default is true (0-based indexing).
1434
1796
  */
1435
- pageStartFromZero = input(true, { ...(ngDevMode ? { debugName: "pageStartFromZero" } : {}), transform: booleanAttribute });
1797
+ pageStartFromZero = input(this.defaults.pageStartFromZero, { ...(ngDevMode ? { debugName: "pageStartFromZero" } : {}), transform: booleanAttribute });
1436
1798
  /**
1437
1799
  * Event emitted when requesting data for a new page.
1438
1800
  *
@@ -1474,41 +1836,88 @@ class DataGrid {
1474
1836
  * Contains the clicked row data and its index.
1475
1837
  */
1476
1838
  rowClick = output();
1839
+ /**
1840
+ * Event emitted when a row is right-clicked (context menu).
1841
+ *
1842
+ * Contains the clicked row data, its index, and the native mouse event.
1843
+ */
1844
+ rowContext = output();
1845
+ /**
1846
+ * Event emitted when a row is double-clicked.
1847
+ *
1848
+ * Contains the clicked row data, its index, and the native mouse event.
1849
+ */
1850
+ rowDoubleClick = output();
1477
1851
  /**
1478
1852
  * Event emitted when a cell is clicked.
1479
1853
  *
1480
1854
  * Contains the clicked row, column configuration, and row index.
1481
1855
  */
1482
1856
  cellClick = output();
1857
+ /**
1858
+ * Event emitted when a cell is right-clicked (context menu).
1859
+ *
1860
+ * Contains the clicked row, column configuration, row index, and the native mouse event.
1861
+ */
1862
+ cellContext = output();
1863
+ /**
1864
+ * Event emitted when a cell is double-clicked.
1865
+ *
1866
+ * Contains the clicked row, column configuration, row index, and the native mouse event.
1867
+ */
1868
+ cellDoubleClick = output();
1483
1869
  vm = inject((DataGridVm));
1484
1870
  selector = new Selector();
1485
1871
  rootEl = viewChild('root', ...(ngDevMode ? [{ debugName: "rootEl" }] : []));
1486
1872
  scrollEl = viewChild('scroll', ...(ngDevMode ? [{ debugName: "scrollEl" }] : []));
1487
1873
  headerEl = viewChild('header', ...(ngDevMode ? [{ debugName: "headerEl" }] : []));
1874
+ ngZone = inject(NgZone);
1488
1875
  cellTypedSlotRefs = contentChildren(DataGridTypeCellTemplateDirective, ...(ngDevMode ? [{ debugName: "cellTypedSlotRefs" }] : []));
1489
1876
  cellDataSlotRefs = contentChildren(DataGridCellTemplateDirective, ...(ngDevMode ? [{ debugName: "cellDataSlotRefs" }] : []));
1877
+ declarativeColumnRefs = contentChildren(DataGridDeclarativeColumn, ...(ngDevMode ? [{ debugName: "declarativeColumnRefs" }] : []));
1490
1878
  headerSlotRefs = contentChildren(DataGridHeaderTemplateDirective, ...(ngDevMode ? [{ debugName: "headerSlotRefs" }] : []));
1491
1879
  emptySlotRefs = contentChildren(DataGridCellEmptyDirective, ...(ngDevMode ? [{ debugName: "emptySlotRefs" }] : []));
1492
1880
  loadingSlotRefs = contentChildren(DataGridCellLoadingDirective, ...(ngDevMode ? [{ debugName: "loadingSlotRefs" }] : []));
1493
1881
  sortIcSlotRefs = contentChildren(DataGridSortIconDirective, ...(ngDevMode ? [{ debugName: "sortIcSlotRefs" }] : []));
1494
1882
  expanderIcSlotRefs = contentChildren(DataGridExpanderIconDirective, ...(ngDevMode ? [{ debugName: "expanderIcSlotRefs" }] : []));
1883
+ stickyRowSlotRefs = contentChildren(DataGridStickyRowDirective, ...(ngDevMode ? [{ debugName: "stickyRowSlotRefs" }] : []));
1884
+ rowSlotRefs = contentChildren(DataGridRowDirective, ...(ngDevMode ? [{ debugName: "rowSlotRefs" }] : []));
1495
1885
  emptyTpl = computed(() => this.emptySlotRefs()?.[0], ...(ngDevMode ? [{ debugName: "emptyTpl" }] : []));
1496
1886
  loadingTpl = computed(() => this.loadingSlotRefs()?.[0], ...(ngDevMode ? [{ debugName: "loadingTpl" }] : []));
1497
1887
  sortTpl = computed(() => this.sortIcSlotRefs()?.at(0), ...(ngDevMode ? [{ debugName: "sortTpl" }] : []));
1498
1888
  expanderTpl = computed(() => this.expanderIcSlotRefs()?.at(0), ...(ngDevMode ? [{ debugName: "expanderTpl" }] : []));
1499
- visibleRows = signal([], ...(ngDevMode ? [{ debugName: "visibleRows" }] : []));
1889
+ stickyRowTpl = computed(() => this.stickyRowSlotRefs()?.at(0), ...(ngDevMode ? [{ debugName: "stickyRowTpl" }] : []));
1890
+ rowTpl = computed(() => this.rowSlotRefs()?.at(0), ...(ngDevMode ? [{ debugName: "rowTpl" }] : []));
1891
+ renderSlots = signal([], ...(ngDevMode ? [{ debugName: "renderSlots" }] : []));
1892
+ renderCount = 0;
1893
+ lastStartIndex = -1;
1894
+ lastEndIndex = -1;
1500
1895
  startIndex = 0;
1501
- headerHeight = signal(40, ...(ngDevMode ? [{ debugName: "headerHeight" }] : []));
1502
- // todo someday
1503
- // protected fillHeight = signal<number | null>(null);
1896
+ headerHeight = signal(this.defaults.headerHeight, ...(ngDevMode ? [{ debugName: "headerHeight" }] : []));
1897
+ stickyRowIndex = signal(null, ...(ngDevMode ? [{ debugName: "stickyRowIndex" }] : []));
1898
+ stickyRowTopPx = signal(0, ...(ngDevMode ? [{ debugName: "stickyRowTopPx" }] : []));
1899
+ stickyRowData = computed(() => {
1900
+ const index = this.stickyRowIndex();
1901
+ const rows = this.data();
1902
+ if (index === null || index < 0 || index >= rows.length) {
1903
+ return null;
1904
+ }
1905
+ return rows[index];
1906
+ }, ...(ngDevMode ? [{ debugName: "stickyRowData" }] : []));
1907
+ stickyIndexes = signal([], ...(ngDevMode ? [{ debugName: "stickyIndexes" }] : []));
1504
1908
  expanderMap = signal(new Map(), ...(ngDevMode ? [{ debugName: "expanderMap" }] : []));
1909
+ declarativeColumns = computed(() => normalizeDeclarativeColumns(this.declarativeColumnRefs().map((columnRef) => columnRef.toDeclarativeColumn()), this.rowKey()), ...(ngDevMode ? [{ debugName: "declarativeColumns" }] : []));
1910
+ sourceColumns = computed(() => {
1911
+ const declarativeColumns = this.declarativeColumns().columns;
1912
+ return declarativeColumns.length ? declarativeColumns : this.columns();
1913
+ }, ...(ngDevMode ? [{ debugName: "sourceColumns" }] : []));
1505
1914
  extendedColumns = computed(() => {
1506
1915
  const hasSelection = this.selection().mode !== 'none';
1507
1916
  const hasIndex = this.hasIndexColumn();
1508
1917
  const newColumns = [];
1509
1918
  hasSelection && newColumns.push(GRID_CHECKBOX_COLUMN);
1510
1919
  hasIndex && newColumns.push(GRID_INDEX_COLUMN);
1511
- return [...newColumns, ...this.columns()];
1920
+ return [...newColumns, ...this.sourceColumns()];
1512
1921
  }, ...(ngDevMode ? [{ debugName: "extendedColumns" }] : []));
1513
1922
  /**
1514
1923
  * Computed CSS height value based on height setting.
@@ -1517,11 +1926,6 @@ class DataGrid {
1517
1926
  const height = this.height();
1518
1927
  if (typeof height === 'number') {
1519
1928
  return `${height}px`;
1520
- // todo someday
1521
- // } else if (height === 'fill') {
1522
- // const h = this.fillHeight();
1523
- //
1524
- // return h === null ? null : `${h}px`;
1525
1929
  }
1526
1930
  else if (height === 'full') {
1527
1931
  return '100%';
@@ -1529,10 +1933,18 @@ class DataGrid {
1529
1933
  return 'var(--re-data-grid-height)';
1530
1934
  }, ...(ngDevMode ? [{ debugName: "styleHeight" }] : []));
1531
1935
  hideSbTimeout;
1936
+ scrollRafId = null;
1937
+ scrollbarRafId = null;
1938
+ stickyRafId = null;
1939
+ lastInfinityPageRequested = null;
1940
+ lastInfinityTotal = -1;
1532
1941
  currentSortField;
1533
1942
  currentSortOrder = 'asc';
1534
1943
  subscription = new Subscription();
1535
1944
  observer;
1945
+ resizeSubject = new Subject();
1946
+ lastResizeWidth = -1;
1947
+ lastResizeHeight = -1;
1536
1948
  constructor() {
1537
1949
  afterRenderEffect(() => {
1538
1950
  this.initRefs();
@@ -1550,19 +1962,60 @@ class DataGrid {
1550
1962
  this.data();
1551
1963
  this.rowHeight();
1552
1964
  this.pinnedRows();
1965
+ this.isRowSticky();
1966
+ this.getRowTemplate();
1967
+ this.loading();
1968
+ this.mode();
1553
1969
  this.vm.containerWidth();
1554
1970
  this.onVerticalScroll(true);
1555
1971
  });
1972
+ effect(() => {
1973
+ const total = this.data()?.length ?? 0;
1974
+ if (total !== this.lastInfinityTotal) {
1975
+ this.lastInfinityTotal = total;
1976
+ this.lastInfinityPageRequested = null;
1977
+ }
1978
+ });
1556
1979
  effect(() => {
1557
1980
  const selection = this.selection();
1558
1981
  if ('defaultSelected' in selection) {
1559
1982
  this.selector.selectedKeys.set(selection.defaultSelected || []);
1560
1983
  }
1561
1984
  });
1985
+ effect(() => {
1986
+ const selection = this.selection();
1987
+ if (selection.mode === 'none' || !('key' in selection)) {
1988
+ return;
1989
+ }
1990
+ const data = this.data() ?? [];
1991
+ const key = selection.key;
1992
+ const keySet = new Set(data.map((row) => row[key]));
1993
+ this.selector.selectedKeys.update((prev) => prev.filter((k) => keySet.has(k)));
1994
+ });
1995
+ effect(() => {
1996
+ const data = this.data() ?? [];
1997
+ const predicate = this.isRowSticky();
1998
+ if (!predicate || !data.length) {
1999
+ this.stickyIndexes.set([]);
2000
+ this.updateStickyFromScroll();
2001
+ return;
2002
+ }
2003
+ const indexes = [];
2004
+ for (let i = 0; i < data.length; i++) {
2005
+ if (predicate(data[i], i)) {
2006
+ indexes.push(i);
2007
+ }
2008
+ }
2009
+ this.stickyIndexes.set(indexes);
2010
+ this.updateStickyFromScroll();
2011
+ });
1562
2012
  inject(DestroyRef).onDestroy(() => {
1563
2013
  this.subscription.unsubscribe();
1564
2014
  this.observer?.disconnect();
1565
2015
  clearTimeout(this.hideSbTimeout);
2016
+ this.clearScrollRaf();
2017
+ this.clearScrollbarRaf();
2018
+ this.clearStickyRaf();
1566
2019
  });
1567
2020
  }
1568
2021
  resolvePinnedData(pr) {
@@ -1599,14 +2052,21 @@ class DataGrid {
1599
2052
  * @param row - Data row that was clicked
1600
2053
  * @param col - Column configuration of the clicked cell
1601
2054
  * @param index - Row index in the dataset
2055
+ * @param event
1602
2056
  */
1603
- onCellClick(row, col, index) {
1604
- this.cellClick.emit({ row, col, index });
2057
+ onCellClick(row, col, index, event) {
2058
+ this.cellClick.emit({ row, col, index, event });
1605
2059
  if (this.selection().mode !== 'none') {
1606
2060
  const selected = this.selector.select(row);
1607
2061
  this.selectChange.emit({ selected });
1608
2062
  }
1609
2063
  }
2064
+ onCellContext(row, col, index, event) {
2065
+ this.cellContext.emit({ row, col, index, event });
2066
+ }
2067
+ onCellDoubleClick(row, col, index, event) {
2068
+ this.cellDoubleClick.emit({ row, col, index, event });
2069
+ }
1610
2070
  isExpandable(column) {
1611
2071
  const keys = this.expanderMap();
1612
2072
  return keys.has(column.key);
@@ -1648,6 +2108,7 @@ class DataGrid {
1648
2108
  const total = data.length ?? 0;
1649
2109
  const rowHeight = Math.max(1, this.rowHeight());
1650
2110
  const virtualBuffer = Math.max(0, this.virtualBuffer());
2111
+ const pinnedTopH = this.vm.pinnedTop().length * rowHeight;
1651
2112
  const limit = this.mode() === 'pagination';
1652
2113
  const cap = limit ? Math.max(1, this.pageSize()) : Number.POSITIVE_INFINITY;
1653
2114
  const viewportRows = Math.ceil(viewportHeight / rowHeight);
@@ -1655,24 +2116,42 @@ class DataGrid {
1655
2116
  const start = Math.max(0, Math.floor(scrollTop / rowHeight) - Math.floor(virtualBuffer / 2));
1656
2117
  const end = Math.min(total, start + visibleCount);
1657
2118
  if (!el || viewportHeight <= 0 || !isFinite(viewportHeight) || !isFinite(rowHeight)) {
1658
- this.visibleRows.set(data);
1659
- this.vm.calcScrollbar();
2119
+ const fallbackCount = Math.min(total, Math.max(1, this.pageSize()));
2120
+ this.renderCount = fallbackCount;
2121
+ this.ensureRenderSlots(fallbackCount);
2122
+ this.updateStickyRow(scrollTop, rowHeight, pinnedTopH);
2123
+ this.scheduleScrollbarUpdate();
1660
2124
  this.showScrollbar();
1661
2125
  this.hideScrollbarSoon();
1662
2126
  return;
1663
2127
  }
2128
+ if (start === this.lastStartIndex && end === this.lastEndIndex) {
2129
+ this.scheduleScrollbarUpdate();
2130
+ this.showScrollbar();
2131
+ this.hideScrollbarSoon();
2132
+ return;
2133
+ }
2134
+ this.lastStartIndex = start;
2135
+ this.lastEndIndex = end;
1664
2136
  this.startIndex = start;
1665
- this.visibleRows.set(data.slice(start, end));
2137
+ this.renderCount = Math.max(0, end - start);
2138
+ this.ensureRenderSlots(this.renderCount);
2139
+ this.updateStickyRow(scrollTop, rowHeight, pinnedTopH);
1666
2140
  if (!initial && this.mode() === 'infinity') {
1667
2141
  const threshold = Math.max(0, total - Math.max(1, Math.floor(visibleCount * 1.5)));
1668
2142
  const nearEnd = start + visibleCount >= threshold;
1669
2143
  if (nearEnd && !this.loading()) {
1670
2144
  const modifier = this.pageStartFromZero() ? 0 : 1;
1671
2145
  const page = Math.floor(total / this.pageSize()) + modifier;
2146
+ if (this.lastInfinityPageRequested === page && this.lastInfinityTotal === total) {
2147
+ return;
2148
+ }
2149
+ this.lastInfinityTotal = total;
2150
+ this.lastInfinityPageRequested = page;
1672
2151
  this.pageChange.emit({ page, rows: this.pageSize() });
1673
2152
  }
1674
2153
  }
1675
- this.vm.calcScrollbar();
2154
+ this.scheduleScrollbarUpdate();
1676
2155
  this.showScrollbar();
1677
2156
  this.hideScrollbarSoon();
1678
2157
  }
@@ -1727,15 +2206,7 @@ class DataGrid {
1727
2206
  clearTimeout(this.hideSbTimeout);
1728
2207
  this.hideSbTimeout = setTimeout(() => this.vm.scrollbarVisible.set(false), delay);
1729
2208
  }
1730
- // todo
1731
2209
  trackPinnedRow = (row) => row.order;
1732
- trackByRow = (row) => {
1733
- const rowKey = this.rowKey();
1734
- if (!rowKey) {
1735
- return row;
1736
- }
1737
- return typeof rowKey === 'function' ? rowKey(row) : row[rowKey];
1738
- };
1739
2210
  cellClass(col, row) {
1740
2211
  if (typeof col.cellClass === 'function') {
1741
2212
  return col.cellClass(row);
@@ -1748,22 +2219,115 @@ class DataGrid {
1748
2219
  }
1749
2220
  return this.currentSortOrder === 'asc' ? 'ascending' : 'descending';
1750
2221
  }
2222
+ tooltipEl = viewChild('tooltip', ...(ngDevMode ? [{ debugName: "tooltipEl" }] : []));
2223
+ tooltipState = signal({
2224
+ text: '',
2225
+ x: 0,
2226
+ y: 0,
2227
+ visible: false,
2228
+ }, ...(ngDevMode ? [{ debugName: "tooltipState" }] : []));
2229
+ resolveTooltip(row, col) {
2230
+ const tooltip = col.tooltip;
2231
+ if (!tooltip) {
2232
+ return null;
2233
+ }
2234
+ if (typeof tooltip === 'function') {
2235
+ const value = tooltip(row);
2236
+ if (value === null || value === undefined || value === '') {
2237
+ return null;
2238
+ }
2239
+ return String(value);
2240
+ }
2241
+ if (typeof tooltip === 'string') {
2242
+ return tooltip === '' ? null : tooltip;
2243
+ }
2244
+ return tooltip;
2245
+ }
2246
+ showTooltip(event, row, col, index) {
2247
+ const resolved = this.resolveTooltip(row, col);
2248
+ if (!resolved) {
2249
+ return;
2250
+ }
2251
+ const baseValue = 'value' in col ? col.value(row) : row[col.key];
2252
+ const ctx = {
2253
+ $implicit: row,
2254
+ row,
2255
+ col: col,
2256
+ index,
2257
+ value: baseValue,
2258
+ };
2259
+ const offset = 12;
2260
+ const x = event.clientX + offset;
2261
+ const y = event.clientY + offset;
2262
+ if (typeof resolved === 'string') {
2263
+ this.tooltipState.set({ text: resolved, x, y, visible: true });
2264
+ }
2265
+ else {
2266
+ this.tooltipState.set({ tpl: resolved, ctx, x, y, visible: true });
2267
+ }
2268
+ requestAnimationFrame(() => this.positionTooltip());
2269
+ }
2270
+ hideTooltip() {
2271
+ if (!this.tooltipState().visible) {
2272
+ return;
2273
+ }
2274
+ this.tooltipState.update((prev) => ({ ...prev, visible: false }));
2275
+ }
2276
+ positionTooltip() {
2277
+ const el = this.tooltipEl()?.nativeElement;
2278
+ const state = this.tooltipState();
2279
+ if (!el || !state.visible) {
2280
+ return;
2281
+ }
2282
+ const rect = el.getBoundingClientRect();
2283
+ const padding = 8;
2284
+ const vw = window.innerWidth;
2285
+ const vh = window.innerHeight;
2286
+ let x = state.x;
2287
+ let y = state.y;
2288
+ if (x + rect.width + padding > vw) {
2289
+ x = Math.max(padding, vw - rect.width - padding);
2290
+ }
2291
+ if (y + rect.height + padding > vh) {
2292
+ y = Math.max(padding, vh - rect.height - padding);
2293
+ }
2294
+ if (x !== state.x || y !== state.y) {
2295
+ this.tooltipState.update((prev) => ({ ...prev, x, y }));
2296
+ }
2297
+ }
2298
+ isStickyRowIndex(index) {
2299
+ return this.stickyRowIndex() === index;
2300
+ }
2301
+ resolveRowTemplate(row, index) {
2302
+ const resolver = this.getRowTemplate();
2303
+ if (resolver) {
2304
+ const resolved = resolver(row, index);
2305
+ if (resolved) {
2306
+ return resolved;
2307
+ }
2308
+ }
2309
+ return this.rowTpl()?.tpl ?? null;
2310
+ }
1751
2311
  initVm() {
1752
2312
  this.vm.scrollEl.set(this.scrollEl());
1753
2313
  this.vm.columns.set(this.extendedColumns());
1754
2314
  this.vm.pinnedRows.set(this.pinnedRows());
2315
+ this.vm.headerGroups.set(this.headerGroups());
2316
+ queueMicrotask(() => this.initHeader());
1755
2317
  }
1756
2318
  initSelector() {
1757
2319
  this.selector.data.set(this.data());
1758
2320
  this.selector.selection.set(this.selection());
1759
2321
  }
1760
2322
  initRefs() {
2323
+ this.vm.globalTypeCellTpls.clear();
2324
+ this.vm.globalDataCellTpls.clear();
2325
+ this.vm.globalRowCellTpls.clear();
1761
2326
  this.cellTypedSlotRefs()?.forEach((dir) => this.vm.globalTypeCellTpls.set(dir.type(), dir.tpl));
1762
2327
  this.cellDataSlotRefs()?.forEach((dir) => this.vm.globalDataCellTpls.set(dir.key(), dir.tpl));
2328
+ this.declarativeColumns().rowCellTemplatesByKey.forEach((tpl, key) => this.vm.globalRowCellTpls.set(key, tpl));
1763
2329
  const headerMap = new Map();
1764
- const cellsMap = new Map();
1765
2330
  this.headerSlotRefs()?.forEach((h) => headerMap.set(h.key(), h.tpl));
1766
- this.cellDataSlotRefs()?.forEach((h) => cellsMap.set(h.key(), h.tpl));
1767
2331
  this.extendedColumns()?.forEach((c) => {
1768
2332
  if (!c.headerTemplate && headerMap.has(c.key)) {
1769
2333
  c.headerTemplate = headerMap.get(c.key);
@@ -1787,11 +2351,9 @@ class DataGrid {
1787
2351
  const scrollEl = this.scrollEl()?.nativeElement;
1788
2352
  if (scrollEl) {
1789
2353
  const scrollDetector = new ScrollDirectionDetector(scrollEl);
1790
- this.subscription = fromEvent(scrollEl, 'scroll')
1791
- .pipe(auditTime(16))
1792
- .subscribe(() => {
1793
- const direction = scrollDetector.detect();
1794
- direction === 'vertical' ? this.onVerticalScroll() : this.onHorizontalScroll();
2354
+ this.ngZone.runOutsideAngular(() => {
2355
+ this.subscription.add(fromEvent(scrollEl, 'scroll').subscribe(() => this.scheduleScrollTick(scrollDetector)));
2356
+ this.subscription.add(fromEvent(scrollEl, 'scroll').subscribe(() => this.scheduleStickyUpdate()));
1795
2357
  });
1796
2358
  }
1797
2359
  queueMicrotask(() => this.onVerticalScroll(true));
@@ -1800,13 +2362,26 @@ class DataGrid {
1800
2362
  const root = this.rootEl();
1801
2363
  if (!root)
1802
2364
  return;
1803
- this.observer = new ResizeObserver((entries) => {
1804
- const width = entries[0].contentRect.width;
1805
- this.onVerticalScroll(true);
1806
- this.onHorizontalScroll();
1807
- this.vm.calcScrollbar();
1808
- this.vm.containerWidth.set(width);
1809
- });
2365
+ this.subscription.add(this.resizeSubject.pipe(debounceTime(this.defaults.debounce.resize)).subscribe((entries) => {
2366
+ const rect = entries[0].contentRect;
2367
+ const width = rect.width;
2368
+ const height = rect.height;
2369
+ const widthChanged = width !== this.lastResizeWidth;
2370
+ const heightChanged = height !== this.lastResizeHeight;
2371
+ if (!widthChanged && !heightChanged) {
2372
+ return;
2373
+ }
2374
+ this.lastResizeWidth = width;
2375
+ this.lastResizeHeight = height;
2376
+ if (widthChanged) {
2377
+ this.vm.containerWidth.set(width);
2378
+ queueMicrotask(() => this.initHeader());
2379
+ }
2380
+ if (heightChanged) {
2381
+ this.onVerticalScroll(true);
2382
+ }
2383
+ }));
2384
+ this.observer = new ResizeObserver((entries) => this.resizeSubject.next(entries));
1810
2385
  this.observer.observe(root.nativeElement);
1811
2386
  }
1812
2387
  initExpander() {
@@ -1818,17 +2393,132 @@ class DataGrid {
1818
2393
  this.vm.columns.set(columns);
1819
2394
  this.expanderMap.set(map);
1820
2395
  }
2396
+ scheduleScrollbarUpdate() {
2397
+ if (this.scrollbarRafId !== null) {
2398
+ return;
2399
+ }
2400
+ this.scrollbarRafId = requestAnimationFrame(() => {
2401
+ this.scrollbarRafId = null;
2402
+ this.vm.calcScrollbar();
2403
+ });
2404
+ }
2405
+ scheduleScrollTick(detector) {
2406
+ if (this.scrollRafId !== null) {
2407
+ return;
2408
+ }
2409
+ this.scrollRafId = requestAnimationFrame(() => {
2410
+ this.scrollRafId = null;
2411
+ const direction = detector.detect();
2412
+ this.ngZone.run(() => {
2413
+ direction === 'vertical' ? this.onVerticalScroll() : this.onHorizontalScroll();
2414
+ this.hideTooltip();
2415
+ });
2416
+ });
2417
+ }
2418
+ clearScrollRaf() {
2419
+ if (this.scrollRafId !== null) {
2420
+ cancelAnimationFrame(this.scrollRafId);
2421
+ this.scrollRafId = null;
2422
+ }
2423
+ }
2424
+ ensureRenderSlots(count) {
2425
+ const current = this.renderSlots();
2426
+ if (current.length === count) {
2427
+ return;
2428
+ }
2429
+ const next = new Array(count);
2430
+ for (let i = 0; i < count; i++) {
2431
+ next[i] = i;
2432
+ }
2433
+ this.renderSlots.set(next);
2434
+ }
2435
+ clearScrollbarRaf() {
2436
+ if (this.scrollbarRafId !== null) {
2437
+ cancelAnimationFrame(this.scrollbarRafId);
2438
+ this.scrollbarRafId = null;
2439
+ }
2440
+ }
2441
+ scheduleStickyUpdate() {
2442
+ if (this.stickyRafId !== null) {
2443
+ return;
2444
+ }
2445
+ this.stickyRafId = requestAnimationFrame(() => {
2446
+ this.stickyRafId = null;
2447
+ this.ngZone.run(() => this.updateStickyFromScroll());
2448
+ });
2449
+ }
2450
+ clearStickyRaf() {
2451
+ if (this.stickyRafId !== null) {
2452
+ cancelAnimationFrame(this.stickyRafId);
2453
+ this.stickyRafId = null;
2454
+ }
2455
+ }
2456
+ updateStickyRow(scrollTop, rowHeight, pinnedTopH) {
2457
+ const indexes = this.stickyIndexes();
2458
+ if (!indexes.length) {
2459
+ this.stickyRowIndex.set(null);
2460
+ return;
2461
+ }
2462
+ const scrollRowIndex = Math.max(0, Math.floor(scrollTop / rowHeight));
2463
+ const currentIndex = this.findStickyIndexBefore(indexes, scrollRowIndex);
2464
+ if (currentIndex === null) {
2465
+ this.stickyRowIndex.set(null);
2466
+ return;
2467
+ }
2468
+ let activeIndex = currentIndex;
2469
+ const prevIndex = this.stickyRowIndex();
2470
+ const hysteresisPx = Math.max(1, rowHeight * 0.5);
2471
+ if (prevIndex !== null && prevIndex !== currentIndex) {
2472
+ const boundaryPx = currentIndex * rowHeight;
2473
+ const delta = Math.abs(scrollTop - boundaryPx);
2474
+ if (delta < hysteresisPx) {
2475
+ activeIndex = prevIndex;
2476
+ }
2477
+ }
2478
+ const stickyTop = this.headerHeight() + pinnedTopH;
2479
+ let topPx = scrollTop + stickyTop;
2480
+ // Keep the sticky row fixed at the sticky top until the next sticky row replaces it.
2481
+ topPx = Math.max(topPx, scrollTop + stickyTop);
2482
+ this.stickyRowIndex.set(activeIndex);
2483
+ this.stickyRowTopPx.set(topPx);
2484
+ }
2485
+ updateStickyFromScroll() {
2486
+ const el = this.scrollEl()?.nativeElement;
2487
+ if (!el)
2488
+ return;
2489
+ const scrollTop = el.scrollTop ?? 0;
2490
+ const rowHeight = Math.max(1, this.rowHeight());
2491
+ const pinnedTopH = this.vm.pinnedTop().length * rowHeight;
2492
+ this.updateStickyRow(scrollTop, rowHeight, pinnedTopH);
2493
+ }
2494
+ findStickyIndexBefore(indexes, index) {
2495
+ let low = 0;
2496
+ let high = indexes.length - 1;
2497
+ let result = -1;
2498
+ while (low <= high) {
2499
+ const mid = (low + high) >>> 1;
2500
+ const value = indexes[mid];
2501
+ if (value <= index) {
2502
+ result = value;
2503
+ low = mid + 1;
2504
+ }
2505
+ else {
2506
+ high = mid - 1;
2507
+ }
2508
+ }
2509
+ return result === -1 ? null : result;
2510
+ }
1821
2511
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.1", ngImport: i0, type: DataGrid, deps: [], target: i0.ɵɵFactoryTarget.Component });
1822
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.1.1", type: DataGrid, isStandalone: true, selector: "re-data-grid", inputs: { data: { classPropertyName: "data", publicName: "data", isSignal: true, isRequired: false, transformFunction: null }, columns: { classPropertyName: "columns", publicName: "columns", isSignal: true, isRequired: false, transformFunction: null }, mode: { classPropertyName: "mode", publicName: "mode", isSignal: true, isRequired: false, transformFunction: null }, pinnedRows: { classPropertyName: "pinnedRows", publicName: "pinnedRows", isSignal: true, isRequired: false, transformFunction: null }, hasIndexColumn: { classPropertyName: "hasIndexColumn", publicName: "hasIndexColumn", isSignal: true, isRequired: false, transformFunction: null }, selection: { classPropertyName: "selection", publicName: "selection", isSignal: true, isRequired: false, transformFunction: null }, pageSize: { classPropertyName: "pageSize", publicName: "pageSize", isSignal: true, isRequired: false, transformFunction: null }, rowHeight: { classPropertyName: "rowHeight", publicName: "rowHeight", isSignal: true, isRequired: false, transformFunction: null }, height: { classPropertyName: "height", publicName: "height", isSignal: true, isRequired: false, transformFunction: null }, virtualBuffer: { classPropertyName: "virtualBuffer", publicName: "virtualBuffer", isSignal: true, isRequired: false, transformFunction: null }, loading: { classPropertyName: "loading", publicName: "loading", isSignal: true, isRequired: false, transformFunction: null }, rowKey: { classPropertyName: "rowKey", publicName: "rowKey", isSignal: true, isRequired: false, transformFunction: null }, pageStartFromZero: { classPropertyName: "pageStartFromZero", publicName: "pageStartFromZero", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { pageChange: "pageChange", sortChange: "sortChange", selectChange: "selectChange", rowClick: "rowClick", cellClick: "cellClick" }, providers: [DataGridVm], queries: [{ propertyName: "cellTypedSlotRefs", predicate: DataGridTypeCellTemplateDirective, isSignal: true }, { propertyName: "cellDataSlotRefs", predicate: DataGridCellTemplateDirective, isSignal: true }, { propertyName: "headerSlotRefs", predicate: DataGridHeaderTemplateDirective, isSignal: true }, { propertyName: "emptySlotRefs", predicate: DataGridCellEmptyDirective, isSignal: true }, { propertyName: "loadingSlotRefs", predicate: DataGridCellLoadingDirective, isSignal: true }, { propertyName: "sortIcSlotRefs", predicate: DataGridSortIconDirective, isSignal: true }, { propertyName: "expanderIcSlotRefs", predicate: DataGridExpanderIconDirective, isSignal: true }], viewQueries: [{ propertyName: "rootEl", first: true, predicate: ["root"], descendants: true, isSignal: true }, { propertyName: "scrollEl", first: true, predicate: ["scroll"], descendants: true, isSignal: true }, { propertyName: "headerEl", first: true, predicate: ["header"], descendants: true, isSignal: true }], ngImport: i0, template: "@let items = data();\n@let empty = !loading() && !items?.length;\n@let notEmpty = !!items?.length;\n\n@let pinnedTopH = vm.pinnedTop().length * rowHeight();\n@let pinnedBottomH = vm.pinnedBottom().length * rowHeight();\n\n<div\n #root\n class=\"re-dg-root\"\n [class.loading]=\"loading()\"\n [style.height]=\"styleHeight()\"\n role=\"table\"\n>\n @if (loading()) {\n <div class=\"re-dg-loader\">\n @let loadingTemplate = loadingTpl();\n\n @if (loadingTemplate?.tpl) {\n <ng-container [ngTemplateOutlet]=\"loadingTemplate!.tpl\" />\n } @else {\n <span class=\"re-dg-loader-text\">\u0417\u0430\u0433\u0440\u0443\u0437\u043A\u0430\u2026</span>\n }\n </div>\n }\n\n <div\n #scroll\n class=\"re-dg-body\"\n role=\"rowgroup\"\n (mouseenter)=\"showScrollbar()\"\n (mouseleave)=\"hideScrollbarSoon()\"\n >\n <div class=\"re-dg-header\" role=\"rowgroup\">\n <div #header class=\"re-dg-row re-dg-header-row\" role=\"row\">\n @for (col of vm.columnsToShow(); track col.key) {\n <div\n class=\"re-dg-header-cell\"\n role=\"columnheader\"\n [class.sortable]=\"!!col.sortKey\"\n [class.active-sort]=\"currentSortField && (currentSortField === col.sortKey)\"\n [class.sticky-left]=\"vm.isStickyLeft(col.key)\"\n [class.sticky-right]=\"vm.isStickyRight(col.key)\"\n [style.left.px]=\"vm.stickyOffset(col.key, 'left')\"\n [style.right.px]=\"vm.stickyOffset(col.key, 'right')\"\n [style.width.px]=\"vm.widthByKey(col.key)\"\n [style.min-width.px]=\"col.minWidth || null\"\n [style.max-width.px]=\"col.maxWidth || null\"\n [style.justify-content]=\"col.align || 'left'\"\n [attr.aria-sort]=\"ariaSort(col)\"\n [attr.tabindex]=\"col.sortKey ? 0 : -1\"\n [title]=\"col.header\"\n (click)=\"col.sortKey && onSort(col)\"\n (keydown.enter)=\"col.sortKey && onSort(col)\"\n >\n @if (col.headerTemplate) {\n <ng-container\n [ngTemplateOutlet]=\"col.headerTemplate\"\n [ngTemplateOutletContext]=\"{ $implicit: col.header }\"\n />\n } @else {\n @let isCheckbox = 'type' in col && col.type === 'checkbox';\n @let isMultiSelect = selection().mode === 'multi';\n\n @if (isCheckbox && isMultiSelect) {\n <re-checkbox-ic [state]=\"selector.isAllSelected()\" (click)=\"selector.selectAll()\" />\n } @else {\n <span class=\"re-dg-header-text\">{{ col.header }}</span>\n }\n\n @if (col.sortKey) {\n <span class=\"re-dg-sort-ind\">\n @let direction = currentSortField === col.sortKey ? currentSortOrder : undefined;\n\n @if (sortTpl()) {\n <ng-container\n [ngTemplateOutlet]=\"sortTpl()!.tpl\"\n [ngTemplateOutletContext]=\"{ $implicit: direction }\"\n />\n } @else {\n <re-sort-ic [direction]=\"direction\" />\n }\n </span>\n }\n\n @if (isExpandable(col)) {\n <button (click)=\"$event.stopPropagation(); onExpand(col)\">\n @let expanded = expanderMap().get(col.key);\n\n @if (expanderTpl()) {\n <ng-container\n [ngTemplateOutlet]=\"expanderTpl()!.tpl\"\n [ngTemplateOutletContext]=\"{ $implicit: expanded }\" />\n } @else {\n <re-expand-ic [expanded]=\"expanded\" />\n }\n </button>\n }\n }\n </div>\n }\n </div>\n\n <!-- PINNED TOP ROWS -->\n @if (notEmpty) {\n @for (pr of vm.pinnedTop(); track trackPinnedRow(pr)) {\n <div class=\"re-dg-row re-dg-pinned re-dg-top\" role=\"row\">\n @if (pr.rowTemplate) {\n <ng-container\n [ngTemplateOutlet]=\"pr.rowTemplate!\"\n [ngTemplateOutletContext]=\"{ $implicit: resolvePinnedData(pr) }\"\n />\n } @else {\n @for (col of vm.columnsToShow(); track col.key) {\n <div\n class=\"re-dg-cell\"\n role=\"cell\"\n [class.sticky-left]=\"vm.isStickyLeft(col.key)\"\n [class.sticky-right]=\"vm.isStickyRight(col.key)\"\n [style.left.px]=\"vm.stickyOffset(col.key, 'left')\"\n [style.right.px]=\"vm.stickyOffset(col.key, 'right')\"\n [style.justify-items]=\"col.align || 'left'\"\n [style.height.px]=\"rowHeight()\"\n [style.width.px]=\"vm.widthByKey(col.key)\"\n >\n {{ resolvePinnedData(pr)[col.key] ?? '' }}\n </div>\n }\n }\n </div>\n }\n }\n </div>\n\n @if (empty) {\n @let emptyTemplate = emptyTpl()?.tpl;\n\n <div class=\"re-dg-empty\">\n @if (emptyTemplate) {\n <ng-container [ngTemplateOutlet]=\"emptyTemplate\" />\n } @else {\n <span class=\"re-dg-empty-text\">\u041D\u0435\u0442 \u0434\u0430\u043D\u043D\u044B\u0445</span>\n }\n </div>\n }\n\n <!-- Content -->\n @if (notEmpty) {\n <div class=\"re-dg-spacer\" [style.height.px]=\"items.length * rowHeight() - pinnedBottomH\"></div>\n\n @for (row of visibleRows(); track trackByRow(row); let vi = $index) {\n <div\n class=\"re-dg-row re-dg-data-row\"\n role=\"row\"\n [style.height.px]=\"rowHeight()\"\n [style.top.px]=\"(startIndex + vi) * rowHeight() + pinnedTopH + headerHeight()\"\n [attr.tabindex]=\"0\"\n (click)=\"$event.stopPropagation(); rowClick.emit({ row, index: startIndex + vi })\"\n (keydown.enter)=\"$event.stopPropagation(); rowClick.emit({ row, index: startIndex + vi })\"\n >\n @for (col of vm.columnsToShow(); track col.key) {\n <div\n class=\"re-dg-cell\"\n role=\"cell\"\n [class.expanded]=\"!!col.expandBy\"\n [class]=\"cellClass(col, row)\"\n [class.sticky-left]=\"vm.isStickyLeft(col.key)\"\n [class.sticky-right]=\"vm.isStickyRight(col.key)\"\n [style.left.px]=\"vm.stickyOffset(col.key, 'left')\"\n [style.right.px]=\"vm.stickyOffset(col.key, 'right')\"\n [style.justify-items]=\"col.align || 'left'\"\n [style.text-align]=\"col.align || 'left'\"\n [style.width.px]=\"vm.widthByKey(col.key)\"\n [attr.tabindex]=\"0\"\n (click)=\"onCellClick(row, col, startIndex + vi);\"\n (keydown.enter)=\"onCellClick(row, col, startIndex + vi)\"\n >\n @let isCheckbox = 'type' in col && col.type === 'checkbox';\n\n @if (isCheckbox) {\n <re-checkbox-ic [state]=\"selector.isSelected(row)\" />\n } @else {\n <re-data-grid-cell [index]=\"startIndex + vi\" [item]=\"row\" [column]=\"col\" />\n }\n </div>\n }\n </div>\n }\n }\n\n <!-- PINNED BOTTOM ROWS -->\n @if (notEmpty) {\n <div class=\"re-dg-footer\" role=\"rowgroup\">\n @for (pr of vm.pinnedBottom(); track trackPinnedRow(pr)) {\n <div class=\"re-dg-row re-dg-pinned re-dg-bottom\" role=\"row\">\n @if (pr.rowTemplate) {\n <ng-container\n [ngTemplateOutlet]=\"pr.rowTemplate!\"\n [ngTemplateOutletContext]=\"{ $implicit: resolvePinnedData(pr) }\"\n />\n } @else {\n @for (col of vm.columnsToShow(); track col.key) {\n <div\n class=\"re-dg-cell\"\n role=\"cell\"\n [class.sticky-left]=\"vm.isStickyLeft(col.key)\"\n [class.sticky-right]=\"vm.isStickyRight(col.key)\"\n [style.left.px]=\"vm.stickyOffset(col.key, 'left')\"\n [style.right.px]=\"vm.stickyOffset(col.key, 'right')\"\n [style.justify-items]=\"col.align || 'left'\"\n [style.height.px]=\"rowHeight()\"\n [style.width.px]=\"vm.widthByKey(col.key)\"\n >\n {{ resolvePinnedData(pr)[col.key] ?? '' }}\n </div>\n }\n }\n </div>\n }\n </div>\n }\n </div>\n\n <!-- Overlay scrollbar -->\n <div class=\"re-dg-scrollbar\" [class.visible]=\"vm.scrollbarVisible()\">\n <div\n class=\"re-dg-scrollbar-thumb\"\n role=\"scrollbar\"\n aria-orientation=\"vertical\"\n aria-hidden=\"false\"\n [style.height.px]=\"vm.thumbHeightPx()\"\n [style.transform]=\"'translateY(' + vm.thumbTopPx() + 'px)'\"\n (mousedown)=\"onThumbDown($event)\"\n ></div>\n </div>\n</div>\n\n", styles: [":host{--re-data-grid-min-height: 200px;--re-data-grid-height: 400px;--re-data-grid-rounded: var(--radius-md, 6px);--re-data-grid-separator-color: var(--border-color);--re-data-grid-separator: 1px solid var(--re-data-grid-separator-color);--re-data-grid-surface: var(--surface-primary, #fff);--re-data-grid-active: var(--primary-color, #2a90f4);--re-data-grid-empty-color: #777;--re-data-grid-empty-surface: transparent;--re-data-grid-loading-color: #444;--re-data-grid-loading-surface: rgba(255, 255, 255, .5);--re-data-grid-scrollbar-size: 10px;--re-data-grid-scrollbar-offset: 2px;--re-data-grid-scrollbar-thumb-size: 8px;--re-data-grid-scrollbar-thumb-color: rgba(0, 0, 0, .35);--re-data-grid-scrollbar-thumb-rounded: 4px;--re-data-grid-header-height: 40px;--re-data-grid-header-separator-color: #ccc;--re-data-grid-header-separator: 1px solid var(--re-data-grid-header-separator-color);--re-data-grid-header-surface: #fff;--re-data-grid-header-cell-font-weight: 600;--re-data-grid-header-cell-font-size: .8rem;--re-data-grid-header-cell-color: #000;--re-data-grid-header-cell-surface: #fafafa;--re-data-grid-footer-separator-color: #ccc;--re-data-grid-footer-separator: 1px solid var(--re-data-grid-footer-separator-color);--re-data-grid-footer-surface: #fff;--re-data-grid-row-separator-color: #bbb;--re-data-grid-row-separator: 1px solid var(--re-data-grid-row-separator-color);--re-data-grid-row-odd-surface: var(--re-data-grid-cell-surface);--re-data-grid-column-separator-color: transparent;--re-data-grid-column-separator: 1px solid var(--re-data-grid-column-separator-color);--re-data-grid-column-odd-surface: var(--re-data-grid-cell-surface);--re-data-grid-cell-paddings: .4rem .625rem;--re-data-grid-cell-font-weight: 400;--re-data-grid-cell-font-size: .75rem;--re-data-grid-cell-color: #000;--re-data-grid-cell-surface: #fff;--re-data-grid-sticky-header-cell-surface: #fff;--re-data-grid-sticky-cell-surface: #fdfdfd;--re-data-grid-sticky-cell-left-shadow: 2px 0 2px rgba(0, 0, 0, .03);--re-data-grid-sticky-cell-right-shadow: -2px 0 2px rgba(0, 0, 0, .03);--re-data-grid-pinned-surface: #fcfcfc;--re-data-grid-pinned-separator-color: #eee;--re-data-grid-pinned-separator: 1px solid var(--re-data-grid-pinned-separator-color);--re-data-grid-expander-color: var(--primary-color, currentColor);--re-data-grid-expanded-color: var(--re-data-grid-cell-color, #000);--re-data-grid-expanded-surface: var(--re-data-grid-cell-surface, #fff);display:block;min-height:0}:host,:host *,:host *:before,:host *:after{box-sizing:border-box;outline:none}:host button{outline:none}.re-dg-root{position:relative;display:flex;flex-direction:column;width:100%;min-height:var(--re-data-grid-min-height);border-radius:var(--re-data-grid-rounded);border:var(--re-data-grid-separator)}.re-dg-root.fill{display:block}.re-dg-root.loading{pointer-events:none;-webkit-user-select:none;user-select:none;cursor:wait}.re-dg-root.loading .re-dg-body{overflow:hidden}.re-dg-root.loading .re-dg-scrollbar{display:none!important}.re-dg-root.loading .re-dg-loader{pointer-events:all}.re-dg-body{position:relative;flex:1 1 auto;min-height:0;min-width:0;height:inherit;border:var(--re-data-grid-separator);border-radius:var(--re-data-grid-rounded);background-color:var(--re-data-grid-surface);overflow:auto;scrollbar-width:none;-ms-overflow-style:none}.re-dg-body::-webkit-scrollbar{width:0;height:0}.re-dg-header,.re-dg-footer{position:sticky;z-index:3}.re-dg-header{top:0;background-color:var(--re-data-grid-header-surface)}.re-dg-header-row{min-height:var(--re-data-grid-header-height)}.re-dg-footer{bottom:0;border-radius:0 0 var(--re-data-grid-rounded) var(--re-data-grid-rounded);background-color:var(--re-data-grid-footer-surface)}.re-dg-row{position:relative;display:flex}.re-dg-data-row{position:absolute;left:0;right:0;cursor:default}.re-dg-cell,.re-dg-header-cell{display:flex;flex:0 0 auto;align-items:center;padding:var(--re-data-grid-cell-paddings);border-right:var(--re-data-grid-column-separator);text-overflow:ellipsis;white-space:nowrap;overflow:hidden}.re-dg-cell{width:100%;border-bottom:var(--re-data-grid-row-separator);font-weight:var(--re-data-grid-cell-font-weight);font-size:var(--re-data-grid-cell-font-size);color:var(--re-data-grid-cell-color);background-color:var(--re-data-grid-cell-surface)}.re-dg-row:nth-child(odd) .re-dg-cell{background-color:var(--re-data-grid-row-odd-surface)}.re-dg-cell:nth-child(odd){background-color:var(--re-data-grid-column-odd-surface)}.re-dg-bottom>.re-dg-cell{border-top:var(--re-data-grid-footer-separator)}.re-dg-header-cell{align-items:center;gap:.75rem;border-bottom:var(--re-data-grid-header-separator);font-weight:var(--re-data-grid-header-cell-font-weight);font-size:var(--re-data-grid-header-cell-font-size);color:var(--re-data-grid-header-cell-color);background:var(--re-data-grid-header-cell-surface);-webkit-user-select:none;user-select:none;transition:color .3s ease-in-out}.re-dg-header-cell:first-child{border-radius:var(--re-data-grid-rounded) 0 0 0}.re-dg-header-cell:last-child{border-radius:0 var(--re-data-grid-rounded) 0 0}.re-dg-data-row:last-child .re-dg-cell:first-child{border-radius:0 0 0 var(--re-data-grid-rounded)}.re-dg-data-row:last-child .re-dg-cell:last-child{border-radius:0 0 var(--re-data-grid-rounded) 0}.re-dg-header-cell .re-dg-header-text{display:-webkit-box;max-height:2.4em;line-height:1.2;white-space:normal;-webkit-box-orient:vertical;-webkit-line-clamp:2;overflow:hidden}.re-dg-row.re-dg-pinned>.re-dg-cell{border-bottom:var(--re-data-grid-pinned-separator);background-color:var(--re-data-grid-pinned-surface)}.re-dg-row .re-dg-header-cell.sticky-left{box-shadow:var(--re-data-grid-sticky-cell-left-shadow);background-color:var(--re-data-grid-sticky-header-cell-surface)}.re-dg-row .re-dg-cell.sticky-left{box-shadow:var(--re-data-grid-sticky-cell-left-shadow);background-color:var(--re-data-grid-sticky-cell-surface)}.re-dg-row .re-dg-header-cell.sticky-right{box-shadow:var(--re-data-grid-sticky-cell-right-shadow);background-color:var(--re-data-grid-sticky-header-cell-surface)}.re-dg-row .re-dg-cell.sticky-right{box-shadow:var(--re-data-grid-sticky-cell-right-shadow);background-color:var(--re-data-grid-sticky-cell-surface)}.sticky-left,.sticky-right{position:sticky;z-index:2}.sortable{cursor:pointer}.active-sort{color:var(--re-data-grid-active)}.re-dg-sort-ind{margin-left:6px}.re-dg-data-row .re-dg-cell.expanded{color:var(--re-data-grid-expanded-color);background:var(--re-data-grid-expanded-surface)}.re-dg-empty{position:absolute;inset:0;display:grid;place-items:center;height:inherit;width:100%;border-radius:var(--re-data-grid-rounded);color:var(--re-data-grid-empty-color);background:var(--re-data-grid-empty-surface)}.re-dg-empty-text{width:100%;text-align:center}.re-dg-loader{position:absolute;inset:0;display:grid;place-items:center;height:inherit;border-radius:var(--re-data-grid-rounded);background-color:var(--re-data-grid-loading-surface);color:var(--re-data-grid-loading-color);z-index:5}.re-dg-scrollbar{position:absolute;right:0;top:0;bottom:0;opacity:0;transition:opacity .15s ease-in-out;pointer-events:none;z-index:4}.re-dg-scrollbar.visible{opacity:1}.re-dg-scrollbar-thumb{position:absolute;right:var(--re-data-grid-scrollbar-offset);width:var(--re-data-grid-scrollbar-thumb-size);border-radius:var(--re-data-grid-scrollbar-thumb-rounded);background:var(--re-data-grid-scrollbar-thumb-color);pointer-events:auto;-webkit-user-select:none;user-select:none}.re-dg-spacer{width:1px}.re-dg-top{top:0}.re-dg-bottom{bottom:0}\n"], dependencies: [{ kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "component", type: DataGridCellComponent, selector: "re-data-grid-cell", inputs: ["index", "item", "column"] }, { kind: "component", type: SortIcon, selector: "re-sort-ic", inputs: ["direction"] }, { kind: "component", type: ExpandIcon, selector: "re-expand-ic", inputs: ["expanded"] }, { kind: "component", type: CheckboxIcon, selector: "re-checkbox-ic", inputs: ["state", "disabled"] }] });
2512
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.1.1", type: DataGrid, isStandalone: true, selector: "re-data-grid", inputs: { data: { classPropertyName: "data", publicName: "data", isSignal: true, isRequired: false, transformFunction: null }, columns: { classPropertyName: "columns", publicName: "columns", isSignal: true, isRequired: false, transformFunction: null }, mode: { classPropertyName: "mode", publicName: "mode", isSignal: true, isRequired: false, transformFunction: null }, pinnedRows: { classPropertyName: "pinnedRows", publicName: "pinnedRows", isSignal: true, isRequired: false, transformFunction: null }, isRowSticky: { classPropertyName: "isRowSticky", publicName: "isRowSticky", isSignal: true, isRequired: false, transformFunction: null }, getRowTemplate: { classPropertyName: "getRowTemplate", publicName: "getRowTemplate", isSignal: true, isRequired: false, transformFunction: null }, hasIndexColumn: { classPropertyName: "hasIndexColumn", publicName: "hasIndexColumn", isSignal: true, isRequired: false, transformFunction: null }, selection: { classPropertyName: "selection", publicName: "selection", isSignal: true, isRequired: false, transformFunction: null }, pageSize: { classPropertyName: "pageSize", publicName: "pageSize", isSignal: true, isRequired: false, transformFunction: null }, rowHeight: { classPropertyName: "rowHeight", publicName: "rowHeight", isSignal: true, isRequired: false, transformFunction: null }, height: { classPropertyName: "height", publicName: "height", isSignal: true, isRequired: false, transformFunction: null }, virtualBuffer: { classPropertyName: "virtualBuffer", publicName: "virtualBuffer", isSignal: true, isRequired: false, transformFunction: null }, headerGroups: { classPropertyName: "headerGroups", publicName: "headerGroups", isSignal: true, isRequired: false, transformFunction: null }, loading: { classPropertyName: "loading", publicName: "loading", isSignal: true, isRequired: false, transformFunction: null }, loadingMode: { classPropertyName: "loadingMode", publicName: "loadingMode", isSignal: true, isRequired: false, transformFunction: null }, rowKey: { classPropertyName: "rowKey", publicName: "rowKey", isSignal: true, isRequired: false, transformFunction: null }, pageStartFromZero: { classPropertyName: "pageStartFromZero", publicName: "pageStartFromZero", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { pageChange: "pageChange", sortChange: "sortChange", selectChange: "selectChange", rowClick: "rowClick", rowContext: "rowContext", rowDoubleClick: "rowDoubleClick", cellClick: "cellClick", cellContext: "cellContext", cellDoubleClick: "cellDoubleClick" }, providers: [DataGridVm], queries: [{ propertyName: "cellTypedSlotRefs", predicate: DataGridTypeCellTemplateDirective, isSignal: true }, { propertyName: "cellDataSlotRefs", predicate: DataGridCellTemplateDirective, isSignal: true }, { propertyName: "declarativeColumnRefs", predicate: DataGridDeclarativeColumn, isSignal: true }, { propertyName: "headerSlotRefs", predicate: DataGridHeaderTemplateDirective, isSignal: true }, { propertyName: "emptySlotRefs", predicate: DataGridCellEmptyDirective, isSignal: true }, { propertyName: "loadingSlotRefs", predicate: DataGridCellLoadingDirective, isSignal: true }, { propertyName: "sortIcSlotRefs", predicate: DataGridSortIconDirective, isSignal: true }, { propertyName: "expanderIcSlotRefs", predicate: DataGridExpanderIconDirective, isSignal: true }, { propertyName: "stickyRowSlotRefs", predicate: DataGridStickyRowDirective, isSignal: true }, { propertyName: "rowSlotRefs", predicate: DataGridRowDirective, isSignal: true }], viewQueries: [{ propertyName: "rootEl", first: true, predicate: ["root"], descendants: true, isSignal: true }, { propertyName: "scrollEl", first: true, predicate: ["scroll"], descendants: true, isSignal: true }, { propertyName: "headerEl", first: true, predicate: ["header"], descendants: true, isSignal: true }, { propertyName: "tooltipEl", first: true, predicate: ["tooltip"], descendants: true, isSignal: true }], ngImport: i0, template: "@let items = data();\r\n@let empty = !loading() && !items?.length;\r\n@let notEmpty = !!items?.length;\r\n@let skeletonRowsCount = 4;\r\n@let skeletonMode = loadingMode() === 'skeleton';\r\n@let spinnerMode = loadingMode() === 'spinner';\r\n@let showInfinitySkeleton = loading() && skeletonMode && mode() === 'infinity';\r\n@let showPaginationSkeleton = loading() && skeletonMode && mode() === 'pagination' && !notEmpty;\r\n@let showSpinnerLoading = loading() && spinnerMode;\r\n@let extraInfinitySkeletonRows = showInfinitySkeleton ? skeletonRowsCount : 0;\r\n\r\n@let pinnedTopH = vm.pinnedTop().length * rowHeight();\r\n@let pinnedBottomH = vm.pinnedBottom().length * rowHeight();\r\n@let rowH = rowHeight();\r\n@let contentW = vm.contentWidth();\r\n@let cols = vm.columnsToShow();\r\n@let stickyTop = pinnedTopH + headerHeight();\r\n@let normalizedHeaderGroups = vm.normalizedHeaderGroups();\r\n@let stickyRow = stickyRowData();\r\n@let stickyIndex = stickyRowIndex();\r\n\r\n<div\r\n #root\r\n class=\"re-dg-root\"\r\n [class.loading]=\"showSpinnerLoading\"\r\n [style.height]=\"styleHeight()\"\r\n role=\"table\"\r\n>\r\n @if (showSpinnerLoading) {\r\n <div class=\"re-dg-loader\">\r\n @let loadingTemplate = loadingTpl();\r\n\r\n @if (loadingTemplate?.tpl) {\r\n <ng-container [ngTemplateOutlet]=\"loadingTemplate!.tpl\" />\r\n } @else {\r\n <span class=\"re-dg-loader-spinner\" aria-label=\"Loading\"></span>\r\n }\r\n </div>\r\n }\r\n\r\n <div\r\n #scroll\r\n class=\"re-dg-body\"\r\n role=\"rowgroup\"\r\n (mouseenter)=\"showScrollbar()\"\r\n (mouseleave)=\"hideScrollbarSoon()\"\r\n >\r\n <div class=\"re-dg-header\" role=\"rowgroup\">\r\n <div #header class=\"re-dg-header-rows\">\r\n @if (normalizedHeaderGroups.length) {\r\n <div class=\"re-dg-row re-dg-header-group-row\" role=\"row\" [style.width.px]=\"vm.contentWidth()\" [style.min-width.%]=\"100\">\r\n @for (group of normalizedHeaderGroups; track group.key) {\r\n @let groupStickyLeft = !!group.startKey && !!group.endKey && vm.isStickyLeft(group.startKey) && vm.isStickyLeft(group.endKey);\r\n @let groupStickyRight = !!group.startKey && !!group.endKey && vm.isStickyRight(group.startKey) && vm.isStickyRight(group.endKey);\r\n\r\n <div\r\n class=\"re-dg-header-cell re-dg-header-group-cell\"\r\n role=\"columnheader\"\r\n [class.sticky-left]=\"groupStickyLeft\"\r\n [class.sticky-right]=\"groupStickyRight\"\r\n [style.left.px]=\"groupStickyLeft && group.startKey ? vm.stickyOffset(group.startKey, 'left') : null\"\r\n [style.right.px]=\"groupStickyRight && group.endKey ? vm.stickyOffset(group.endKey, 'right') : null\"\r\n [style.width.px]=\"group.widthPx\"\r\n [style.justify-content]=\"group.align || 'left'\"\r\n [title]=\"group.title || ''\"\r\n >\r\n @if (group.titleTemplate) {\r\n <ng-container\r\n [ngTemplateOutlet]=\"group.titleTemplate\"\r\n [ngTemplateOutletContext]=\"{ $implicit: group.title || '' }\"\r\n />\r\n } @else {\r\n <span class=\"re-dg-header-text\">{{ group.title || '' }}</span>\r\n }\r\n </div>\r\n }\r\n </div>\r\n }\r\n\r\n <div class=\"re-dg-row re-dg-header-row\" role=\"row\" [style.width.px]=\"vm.contentWidth()\" [style.min-width.%]=\"100\">\r\n @for (col of vm.columnsToShow(); track col.key) {\r\n <div\r\n class=\"re-dg-header-cell\"\r\n role=\"columnheader\"\r\n [class.sortable]=\"!!col.sortKey\"\r\n [class.active-sort]=\"currentSortField && (currentSortField === col.sortKey)\"\r\n [class.sticky-left]=\"vm.isStickyLeft(col.key)\"\r\n [class.sticky-right]=\"vm.isStickyRight(col.key)\"\r\n [style.left.px]=\"vm.stickyOffset(col.key, 'left')\"\r\n [style.right.px]=\"vm.stickyOffset(col.key, 'right')\"\r\n [style.width.px]=\"vm.widthByKey(col.key)\"\r\n [style.min-width.px]=\"col.minWidth || null\"\r\n [style.max-width.px]=\"col.maxWidth || null\"\r\n [style.justify-content]=\"col.align || 'left'\"\r\n [attr.aria-sort]=\"ariaSort(col)\"\r\n [attr.tabindex]=\"col.sortKey ? 0 : -1\"\r\n [title]=\"col.header\"\r\n (click)=\"col.sortKey && onSort(col)\"\r\n (keydown.enter)=\"col.sortKey && onSort(col)\"\r\n >\r\n @let isCheckbox = 'type' in col && col.type === 'checkbox';\r\n @let isMultiSelect = selection().mode === 'multi';\r\n\r\n @if (isCheckbox && isMultiSelect) {\r\n <re-checkbox-ic [state]=\"selector.isAllSelected()\" (click)=\"selector.selectAll()\" />\r\n } @else {\r\n @if (col.headerTemplate) {\r\n <ng-container\r\n [ngTemplateOutlet]=\"col.headerTemplate\"\r\n [ngTemplateOutletContext]=\"{ $implicit: col.header }\"\r\n />\r\n } @else {\r\n <span class=\"re-dg-header-text\">{{ col.header }}</span>\r\n }\r\n }\r\n\r\n @if (col.sortKey) {\r\n <span class=\"re-dg-sort-ind\">\r\n @let direction = currentSortField === col.sortKey ? currentSortOrder : undefined;\r\n\r\n @if (sortTpl()) {\r\n <ng-container\r\n [ngTemplateOutlet]=\"sortTpl()!.tpl\"\r\n [ngTemplateOutletContext]=\"{ $implicit: direction }\"\r\n />\r\n } @else {\r\n <re-sort-ic [direction]=\"direction\" />\r\n }\r\n </span>\r\n }\r\n\r\n @if (isExpandable(col)) {\r\n <button (click)=\"$event.stopPropagation(); onExpand(col)\">\r\n @let expanded = expanderMap().get(col.key);\r\n\r\n @if (expanderTpl()) {\r\n <ng-container\r\n [ngTemplateOutlet]=\"expanderTpl()!.tpl\"\r\n [ngTemplateOutletContext]=\"{ $implicit: expanded }\" />\r\n } @else {\r\n <re-expand-ic [expanded]=\"expanded\" />\r\n }\r\n </button>\r\n }\r\n </div>\r\n }\r\n </div>\r\n </div>\r\n\r\n <!-- PINNED TOP ROWS -->\r\n @if (notEmpty) {\r\n @for (pr of vm.pinnedTop(); track trackPinnedRow(pr)) {\r\n <div class=\"re-dg-row re-dg-pinned re-dg-top\" role=\"row\" [style.width.px]=\"contentW\" [style.min-width.%]=\"100\">\r\n @if (pr.rowTemplate) {\r\n <ng-container\r\n [ngTemplateOutlet]=\"pr.rowTemplate!\"\r\n [ngTemplateOutletContext]=\"{ $implicit: resolvePinnedData(pr) }\"\r\n />\r\n } @else {\r\n @for (col of cols; track col.key) {\r\n @let stickyLeft = vm.stickyOffset(col.key, 'left');\r\n @let stickyRight = vm.stickyOffset(col.key, 'right');\r\n @let isLeft = vm.isStickyLeft(col.key);\r\n @let isRight = vm.isStickyRight(col.key);\r\n <div\n class=\"re-dg-cell\"\n role=\"cell\"\n [class.sticky-left]=\"isLeft\"\n [class.sticky-right]=\"isRight\"\n [style.left.px]=\"stickyLeft\"\n [style.right.px]=\"stickyRight\"\n [style.justify-items]=\"col.align || 'left'\"\n [style.height.px]=\"rowH\"\n [style.width.px]=\"vm.widthByKey(col.key)\"\n (mouseenter)=\"showTooltip($event, $any(resolvePinnedData(pr)), col, -1)\"\n (mouseleave)=\"hideTooltip()\"\n >\n {{ resolvePinnedData(pr)[col.key] ?? '' }}\n </div>\n }\r\n }\r\n </div>\r\n }\r\n }\r\n </div>\r\n\r\n <!-- STICKY ROW -->\r\n @if (stickyRow && stickyIndex !== null) {\r\n <div\r\n class=\"re-dg-row re-dg-data-row re-dg-sticky-row\"\r\n role=\"row\"\r\n [style.width.px]=\"vm.contentWidth()\"\r\n [style.min-width.%]=\"100\"\r\n [style.height.px]=\"rowHeight()\"\r\n [style.top.px]=\"stickyRowTopPx()\"\r\n [attr.tabindex]=\"0\"\r\n (click)=\"$event.stopPropagation(); rowClick.emit({ row: stickyRow, index: stickyIndex, event: $event })\"\r\n (contextmenu)=\"$event.stopPropagation(); rowContext.emit({ row: stickyRow, index: stickyIndex, event: $event })\"\r\n (dblclick)=\"$event.stopPropagation(); rowDoubleClick.emit({ row: stickyRow, index: stickyIndex, event: $event })\"\r\n (keydown.enter)=\"$event.stopPropagation(); rowClick.emit({ row: stickyRow, index: stickyIndex, event: $event })\"\r\n >\r\n @let stickyTemplate = stickyRowTpl();\r\n @let rowTemplate = resolveRowTemplate(stickyRow, stickyIndex);\r\n\r\n @if (stickyTemplate?.tpl) {\r\n <ng-container\r\n [ngTemplateOutlet]=\"stickyTemplate!.tpl\"\r\n [ngTemplateOutletContext]=\"{ $implicit: stickyRow, index: stickyIndex }\"\r\n />\r\n } @else if (rowTemplate) {\r\n <ng-container\r\n [ngTemplateOutlet]=\"rowTemplate\"\r\n [ngTemplateOutletContext]=\"{\r\n $implicit: stickyRow,\r\n index: stickyIndex,\r\n columns: vm.columnsToShow(),\r\n rowHeight: rowHeight(),\r\n isSticky: true\r\n }\"\r\n />\r\n } @else {\r\n @for (col of vm.columnsToShow(); track col.key) {\r\n <div\n class=\"re-dg-cell\"\n role=\"cell\"\n [class.expanded]=\"!!col.expandBy\"\n [class]=\"cellClass(col, stickyRow)\"\n [class.sticky-left]=\"vm.isStickyLeft(col.key)\"\n [class.sticky-right]=\"vm.isStickyRight(col.key)\"\n [style.left.px]=\"vm.stickyOffset(col.key, 'left')\"\n [style.right.px]=\"vm.stickyOffset(col.key, 'right')\"\n [style.justify-items]=\"col.align || 'left'\"\n [style.text-align]=\"col.align || 'left'\"\n [style.width.px]=\"vm.widthByKey(col.key)\"\n [attr.tabindex]=\"0\"\n (mouseenter)=\"showTooltip($event, stickyRow, col, stickyIndex)\"\n (mouseleave)=\"hideTooltip()\"\n (click)=\"onCellClick(stickyRow, col, stickyIndex, $event);\"\n (contextmenu)=\"onCellContext(stickyRow, col, stickyIndex, $event)\"\n (dblclick)=\"onCellDoubleClick(stickyRow, col, stickyIndex, $event)\"\n (keydown.enter)=\"onCellClick(stickyRow, col, stickyIndex, $event)\"\n >\n @let isCheckbox = 'type' in col && col.type === 'checkbox';\r\n\r\n @if (isCheckbox) {\r\n <re-checkbox-ic [state]=\"selector.isSelected(stickyRow)\" />\r\n } @else {\r\n <re-data-grid-cell [index]=\"stickyIndex\" [item]=\"stickyRow\" [column]=\"col\" />\r\n }\r\n </div>\r\n }\r\n }\r\n </div>\r\n }\r\n\r\n @if (empty) {\r\n @let emptyTemplate = emptyTpl()?.tpl;\r\n\r\n <div class=\"re-dg-empty\">\r\n @if (emptyTemplate) {\r\n <ng-container [ngTemplateOutlet]=\"emptyTemplate\" />\r\n } @else {\r\n <span class=\"re-dg-empty-text\">{{ defaults.translations.emptyState }}</span>\r\n }\r\n </div>\r\n }\r\n\r\n <!-- Content -->\r\n @if (notEmpty) {\r\n <div\r\n class=\"re-dg-spacer\"\r\n [style.width.px]=\"contentW\"\r\n [style.height.px]=\"(items.length + extraInfinitySkeletonRows) * rowH - pinnedBottomH\"></div>\r\n\r\n @for (slot of renderSlots(); track slot) {\r\n @let rowIndex = startIndex + slot;\r\n @let row = items[rowIndex];\r\n @let rowTemplate = row ? resolveRowTemplate(row, rowIndex) : null;\r\n\r\n @if (row && !isStickyRowIndex(rowIndex)) {\r\n <div\r\n class=\"re-dg-row re-dg-data-row\"\r\n role=\"row\"\r\n [style.width.px]=\"contentW\"\r\n [style.min-width.%]=\"100\"\r\n [style.height.px]=\"rowH\"\r\n [style.transform]=\"'translateY(' + (rowIndex * rowH + stickyTop) + 'px)'\"\r\n [attr.tabindex]=\"0\"\r\n (click)=\"$event.stopPropagation(); rowClick.emit({ row, index: rowIndex, event: $event })\"\r\n (contextmenu)=\"$event.stopPropagation(); rowContext.emit({ row, index: rowIndex, event: $event })\"\r\n (dblclick)=\"$event.stopPropagation(); rowDoubleClick.emit({ row, index: rowIndex, event: $event })\"\r\n (keydown.enter)=\"$event.stopPropagation(); rowClick.emit({ row, index: rowIndex, event: $event })\"\r\n >\r\n @if (rowTemplate) {\r\n <ng-container\r\n [ngTemplateOutlet]=\"rowTemplate\"\r\n [ngTemplateOutletContext]=\"{\r\n $implicit: row,\r\n index: rowIndex,\r\n columns: vm.columnsToShow(),\r\n rowHeight: rowHeight(),\r\n isSticky: false\r\n }\"\r\n />\r\n } @else {\r\n @for (col of cols; track col.key) {\r\n @let stickyLeft = vm.stickyOffset(col.key, 'left');\r\n @let stickyRight = vm.stickyOffset(col.key, 'right');\r\n @let isLeft = vm.isStickyLeft(col.key);\r\n @let isRight = vm.isStickyRight(col.key);\r\n <div\n class=\"re-dg-cell\"\n role=\"cell\"\n [class.expanded]=\"!!col.expandBy\"\n [class]=\"cellClass(col, row)\"\n [class.sticky-left]=\"isLeft\"\n [class.sticky-right]=\"isRight\"\n [style.left.px]=\"stickyLeft\"\n [style.right.px]=\"stickyRight\"\n [style.justify-items]=\"col.align || 'left'\"\n [style.text-align]=\"col.align || 'left'\"\n [style.width.px]=\"vm.widthByKey(col.key)\"\n [attr.tabindex]=\"0\"\n (mouseenter)=\"showTooltip($event, row, col, rowIndex)\"\n (mouseleave)=\"hideTooltip()\"\n (click)=\"onCellClick(row, col, rowIndex, $event);\"\n (contextmenu)=\"onCellContext(row, col, rowIndex, $event)\"\n (dblclick)=\"onCellDoubleClick(row, col, rowIndex, $event)\"\n (keydown.enter)=\"onCellClick(row, col, rowIndex, $event)\"\n >\n @let isCheckbox = 'type' in col && col.type === 'checkbox';\r\n\r\n @if (isCheckbox) {\r\n <re-checkbox-ic [state]=\"selector.isSelected(row)\" />\r\n } @else {\r\n <re-data-grid-cell [index]=\"rowIndex\" [item]=\"row\" [column]=\"col\" />\r\n }\r\n </div>\r\n }\r\n }\r\n </div>\r\n }\r\n }\r\n }\r\n\r\n @if (showInfinitySkeleton || showPaginationSkeleton) {\r\n @let loadingTemplate = loadingTpl();\r\n\r\n @if (loadingTemplate?.tpl) {\r\n <ng-container [ngTemplateOutlet]=\"loadingTemplate!.tpl\" />\r\n } @else {\r\n @for (si of [0, 1, 2, 3]; track si) {\r\n <div\r\n class=\"re-dg-row re-dg-data-row re-dg-skeleton-row\"\r\n role=\"row\"\r\n [style.width.px]=\"contentW\"\r\n [style.min-width.%]=\"100\"\r\n [style.height.px]=\"rowH\"\r\n [style.transform]=\"'translateY(' + (((showInfinitySkeleton ? items.length : 0) + si) * rowH + stickyTop) + 'px)'\"\r\n >\r\n <span class=\"re-dg-skeleton-row-line\"></span>\r\n </div>\r\n }\r\n }\r\n }\r\n\r\n <!-- PINNED BOTTOM ROWS -->\r\n @if (notEmpty) {\r\n <div class=\"re-dg-footer\" role=\"rowgroup\">\r\n @for (pr of vm.pinnedBottom(); track trackPinnedRow(pr)) {\r\n <div class=\"re-dg-row re-dg-pinned re-dg-bottom\" role=\"row\" [style.width.px]=\"contentW\" [style.min-width.%]=\"100\">\r\n @if (pr.rowTemplate) {\r\n <ng-container\r\n [ngTemplateOutlet]=\"pr.rowTemplate!\"\r\n [ngTemplateOutletContext]=\"{ $implicit: resolvePinnedData(pr) }\"\r\n />\r\n } @else {\r\n @for (col of cols; track col.key) {\r\n @let stickyLeft = vm.stickyOffset(col.key, 'left');\r\n @let stickyRight = vm.stickyOffset(col.key, 'right');\r\n @let isLeft = vm.isStickyLeft(col.key);\r\n @let isRight = vm.isStickyRight(col.key);\r\n <div\n class=\"re-dg-cell\"\n role=\"cell\"\n [class.sticky-left]=\"isLeft\"\n [class.sticky-right]=\"isRight\"\n [style.left.px]=\"stickyLeft\"\n [style.right.px]=\"stickyRight\"\n [style.justify-items]=\"col.align || 'left'\"\n [style.height.px]=\"rowH\"\n [style.width.px]=\"vm.widthByKey(col.key)\"\n (mouseenter)=\"showTooltip($event, $any(resolvePinnedData(pr)), col, -1)\"\n (mouseleave)=\"hideTooltip()\"\n >\n {{ resolvePinnedData(pr)[col.key] ?? '' }}\n </div>\n }\r\n }\r\n </div>\r\n }\r\n </div>\r\n }\r\n </div>\r\n\r\n @let tooltipStateValue = tooltipState();\n <div\n class=\"re-dg-tooltip\"\n #tooltip\n [class.visible]=\"tooltipStateValue.visible\"\n [style.left.px]=\"tooltipStateValue.x\"\n [style.top.px]=\"tooltipStateValue.y\"\n >\n @if (tooltipStateValue.tpl) {\n <ng-container\n [ngTemplateOutlet]=\"tooltipStateValue.tpl\"\n [ngTemplateOutletContext]=\"tooltipStateValue.ctx\"\n />\n } @else {\n {{ tooltipStateValue.text }}\n }\n </div>\n\n <!-- Overlay scrollbar -->\n <div class=\"re-dg-scrollbar\" [class.visible]=\"vm.scrollbarVisible()\">\n <div\r\n class=\"re-dg-scrollbar-thumb\"\r\n role=\"scrollbar\"\r\n aria-orientation=\"vertical\"\r\n aria-hidden=\"false\"\r\n [style.height.px]=\"vm.thumbHeightPx()\"\r\n [style.transform]=\"'translateY(' + vm.thumbTopPx() + 'px)'\"\r\n (mousedown)=\"onThumbDown($event)\"\r\n ></div>\r\n </div>\r\n</div>\r\n", styles: [":host{--re-data-grid-min-height: 200px;--re-data-grid-height: 400px;--re-data-grid-rounded: var(--radius-md, 6px);--re-data-grid-separator-color: var(--border-color);--re-data-grid-separator: 1px solid var(--re-data-grid-separator-color);--re-data-grid-surface: var(--surface-neutral, #fff);--re-data-grid-active: var(--primary-color, #2a90f4);--re-data-grid-empty-color: #777;--re-data-grid-empty-surface: transparent;--re-data-grid-loading-color: #444;--re-data-grid-loading-surface: rgba(255, 255, 255, .5);--re-data-grid-spinner-size: 2rem;--re-data-grid-spinner-width: .25rem;--re-data-grid-spinner-track-color: rgba(0, 0, 0, .12);--re-data-grid-skeleton-width: 100%;--re-data-grid-skeleton-height: 100%;--re-data-grid-skeleton-rounded: var(--re-data-grid-rounded, .75rem);--re-data-grid-skeleton-shine: rgba(255, 255, 255, .8);--re-data-grid-skeleton-line: #e7ebf0;--re-data-grid-scrollbar-size: 4px;--re-data-grid-scrollbar-offset: 2px;--re-data-grid-scrollbar-track-rounded: .25rem;--re-data-grid-scrollbar-track-surface: transparent;--re-data-grid-scrollbar-thumb-size: 8px;--re-data-grid-scrollbar-thumb-color: rgba(0, 0, 0, .25);--re-data-grid-scrollbar-thumb-active-color: rgba(0, 0, 0, .45);--re-data-grid-scrollbar-thumb-rounded: var(--re-data-grid-scrollbar-track-rounded);--re-data-grid-tooltip-surface: #0f172a;--re-data-grid-tooltip-color: #f8fafc;--re-data-grid-tooltip-radius: .5rem;--re-data-grid-tooltip-padding: .4rem .6rem;--re-data-grid-tooltip-shadow: 0 8px 24px rgba(15, 23, 42, .25);--re-data-grid-tooltip-z: 60;--re-data-grid-header-surface: #fff;--re-data-grid-header-group-row-height: var(--re-data-grid-header-row-height);--re-data-grid-header-group-row-separator-color: var(--re-data-grid-header-row-separator-color);--re-data-grid-header-group-row-separator: 1px solid var(--re-data-grid-header-group-row-separator-color);--re-data-grid-header-row-height: 40px;--re-data-grid-header-row-separator-color: #ccc;--re-data-grid-header-row-separator: 1px solid var(--re-data-grid-header-row-separator-color);--re-data-grid-header-group-cell-font-weight: var(--re-data-grid-header-cell-font-weight);--re-data-grid-header-group-cell-font-size: var(--re-data-grid-header-cell-font-size);--re-data-grid-header-group-cell-color: var(--re-data-grid-header-cell-color);--re-data-grid-header-group-cell-surface: var(--re-data-grid-header-cell-surface);--re-data-grid-header-cell-font-weight: 600;--re-data-grid-header-cell-font-size: .8rem;--re-data-grid-header-cell-color: #000;--re-data-grid-header-cell-surface: #fafafa;--re-data-grid-footer-separator-color: #ccc;--re-data-grid-footer-separator: 1px solid var(--re-data-grid-footer-separator-color);--re-data-grid-footer-surface: #fff;--re-data-grid-row-separator-color: #bbb;--re-data-grid-row-separator: 1px solid var(--re-data-grid-row-separator-color);--re-data-grid-row-odd-surface: var(--re-data-grid-cell-surface);--re-data-grid-column-separator-color: transparent;--re-data-grid-column-separator: 1px solid var(--re-data-grid-column-separator-color);--re-data-grid-column-odd-surface: var(--re-data-grid-cell-surface);--re-data-grid-cell-paddings: .4rem .625rem;--re-data-grid-cell-font-weight: 400;--re-data-grid-cell-font-size: .75rem;--re-data-grid-cell-color: #000;--re-data-grid-cell-surface: #fff;--re-data-grid-sticky-header-cell-surface: #fff;--re-data-grid-sticky-cell-surface: #fdfdfd;--re-data-grid-sticky-cell-left-shadow: 2px 0 2px rgba(0, 0, 0, .03);--re-data-grid-sticky-cell-right-shadow: -2px 0 2px rgba(0, 0, 0, .03);--re-data-grid-pinned-surface: #fcfcfc;--re-data-grid-pinned-separator-color: #eee;--re-data-grid-pinned-separator: 1px solid var(--re-data-grid-pinned-separator-color);--re-data-grid-expander-color: var(--primary-color, currentColor);--re-data-grid-expanded-color: var(--re-data-grid-cell-color, #000);--re-data-grid-expanded-surface: var(--re-data-grid-cell-surface, #fff);display:block;min-height:0}:host,:host *,:host *:before,:host *:after{box-sizing:border-box;outline:none}:host button{outline:none}.re-dg-root{position:relative;display:flex;flex-direction:column;width:100%;min-height:var(--re-data-grid-min-height);border-radius:var(--re-data-grid-rounded);border:var(--re-data-grid-separator)}.re-dg-root.fill{display:block}.re-dg-root.loading{pointer-events:none;-webkit-user-select:none;user-select:none;cursor:wait}.re-dg-root.loading .re-dg-body{overflow:hidden}.re-dg-root.loading .re-dg-scrollbar{display:none!important}.re-dg-root.loading .re-dg-loader{pointer-events:all}.re-dg-body{position:relative;flex:1 1 auto;min-height:0;min-width:0;height:inherit;border:var(--re-data-grid-separator);border-radius:var(--re-data-grid-rounded);background-color:var(--re-data-grid-surface);overflow:auto;scrollbar-width:auto;-ms-overflow-style:auto}.re-dg-body::-webkit-scrollbar{width:var(--re-data-grid-scrollbar-size);height:var(--re-data-grid-scrollbar-size)}.re-dg-body::-webkit-scrollbar:vertical{width:0}.re-dg-body::-webkit-scrollbar-track{border-radius:var(--re-data-grid-scrollbar-track-rounded);background:var(--re-data-grid-scrollbar-track-surface)}.re-dg-body::-webkit-scrollbar-thumb{border-radius:var(--re-data-grid-scrollbar-thumb-rounded);background:var(--re-data-grid-scrollbar-thumb-color);transition:opacity .3s ease}.re-dg-body::-webkit-scrollbar-thumb:hover{background:var(--re-data-grid-scrollbar-thumb-active-color)}.re-dg-header,.re-dg-footer{position:sticky;z-index:3}.re-dg-header{top:0;background-color:var(--re-data-grid-header-surface)}.re-dg-header-row{min-height:var(--re-data-grid-header-row-height)}.re-dg-header-group-row{min-height:var(--re-data-grid-header-group-row-height)}.re-dg-header-rows{display:flex;flex-direction:column}.re-dg-footer{bottom:0;border-radius:0 0 var(--re-data-grid-rounded) var(--re-data-grid-rounded);background-color:var(--re-data-grid-footer-surface)}.re-dg-row{position:relative;display:flex}.re-dg-data-row{position:absolute;left:0;top:0;min-width:100%;cursor:default;will-change:transform}.re-dg-sticky-row{z-index:2;top:0}.re-dg-cell,.re-dg-header-cell,.re-dg-pinned-row{display:flex;flex:0 0 auto;align-items:center;padding:var(--re-data-grid-cell-paddings);border-right:var(--re-data-grid-column-separator);text-overflow:ellipsis;white-space:nowrap;overflow:hidden}.re-dg-cell,.re-dg-pinned-row{width:100%;border-bottom:var(--re-data-grid-row-separator);font-weight:var(--re-data-grid-cell-font-weight);font-size:var(--re-data-grid-cell-font-size);color:var(--re-data-grid-cell-color);background-color:var(--re-data-grid-cell-surface)}.re-dg-row:nth-child(odd) .re-dg-cell{background-color:var(--re-data-grid-row-odd-surface)}.re-dg-cell:nth-child(odd){background-color:var(--re-data-grid-column-odd-surface)}.re-dg-bottom>.re-dg-cell{border-top:var(--re-data-grid-footer-separator)}.re-dg-header-cell{align-items:center;gap:.75rem;border-bottom:var(--re-data-grid-header-row-separator);font-weight:var(--re-data-grid-header-cell-font-weight);font-size:var(--re-data-grid-header-cell-font-size);color:var(--re-data-grid-header-cell-color);background:var(--re-data-grid-header-cell-surface);-webkit-user-select:none;user-select:none;transition:color .3s ease-in-out}.re-dg-header-cell.re-dg-header-group-cell{border-bottom:var(--re-data-grid-header-group-row-separator);font-weight:var(--re-data-grid-header-group-cell-font-weight);font-size:var(--re-data-grid-header-group-cell-font-size);color:var(--re-data-grid-header-group-cell-color);background:var(--re-data-grid-header-group-cell-surface)}.re-dg-header-row:first-child .re-dg-header-cell:first-child{border-radius:var(--re-data-grid-rounded) 0 0 0}.re-dg-header-row:first-child .re-dg-header-cell:last-child{border-radius:0 var(--re-data-grid-rounded) 0 0}.re-dg-data-row:last-child .re-dg-cell:first-child{border-radius:0 0 0 var(--re-data-grid-rounded)}.re-dg-data-row:last-child .re-dg-cell:last-child{border-radius:0 0 var(--re-data-grid-rounded) 0}.re-dg-header-cell .re-dg-header-text{display:-webkit-box;max-height:2.4em;line-height:1.2;white-space:normal;-webkit-box-orient:vertical;-webkit-line-clamp:2;overflow:hidden}.re-dg-row.re-dg-pinned>.re-dg-cell{border-bottom:var(--re-data-grid-pinned-separator);background-color:var(--re-data-grid-pinned-surface)}.re-dg-row .re-dg-header-cell.sticky-left{box-shadow:var(--re-data-grid-sticky-cell-left-shadow);background-color:var(--re-data-grid-sticky-header-cell-surface)}.re-dg-row .re-dg-cell.sticky-left{box-shadow:var(--re-data-grid-sticky-cell-left-shadow);background-color:var(--re-data-grid-sticky-cell-surface)}.re-dg-row .re-dg-header-cell.sticky-right{box-shadow:var(--re-data-grid-sticky-cell-right-shadow);background-color:var(--re-data-grid-sticky-header-cell-surface)}.re-dg-row .re-dg-cell.sticky-right{box-shadow:var(--re-data-grid-sticky-cell-right-shadow);background-color:var(--re-data-grid-sticky-cell-surface)}.sticky-left,.sticky-right{position:sticky;z-index:2}.sortable{cursor:pointer}.active-sort{color:var(--re-data-grid-active)}.re-dg-sort-ind{margin-left:6px}.re-dg-data-row .re-dg-cell.expanded{color:var(--re-data-grid-expanded-color);background:var(--re-data-grid-expanded-surface)}.re-dg-empty{position:absolute;inset:0;display:grid;place-items:center;height:inherit;width:100%;border-radius:var(--re-data-grid-rounded);color:var(--re-data-grid-empty-color);background:var(--re-data-grid-empty-surface)}.re-dg-empty-text{width:100%;text-align:center}.re-dg-loader{position:absolute;inset:0;display:grid;place-items:center;height:inherit;border-radius:var(--re-data-grid-rounded);background-color:var(--re-data-grid-loading-surface);color:var(--re-data-grid-loading-color);z-index:5}.re-dg-tooltip{position:fixed;left:0;top:0;max-width:min(28rem,70vw);padding:var(--re-data-grid-tooltip-padding);border-radius:var(--re-data-grid-tooltip-radius);background:var(--re-data-grid-tooltip-surface);color:var(--re-data-grid-tooltip-color);box-shadow:var(--re-data-grid-tooltip-shadow);font-size:.75rem;line-height:1.2;z-index:var(--re-data-grid-tooltip-z);pointer-events:none;opacity:0;transform:translateY(4px);transition:opacity .12s ease,transform .12s ease}.re-dg-tooltip.visible{opacity:1;transform:translateY(0)}.re-dg-loader-spinner{width:var(--re-data-grid-spinner-size);height:var(--re-data-grid-spinner-size);border-radius:50%;border:var(--re-data-grid-spinner-width) solid var(--re-data-grid-spinner-track-color);border-top-color:var(--re-data-grid-loading-color);animation:re-dg-spinner .8s linear infinite}.re-dg-skeleton-row{display:flex;align-items:center;padding:var(--re-data-grid-cell-paddings);border-bottom:var(--re-data-grid-row-separator);background-color:var(--re-data-grid-cell-surface);pointer-events:none}.re-dg-skeleton-row-line{display:block;width:var(--re-data-grid-skeleton-width);height:var(--re-data-grid-skeleton-height);border-radius:var(--re-data-grid-skeleton-rounded);background:linear-gradient(90deg,var(--re-data-grid-skeleton-line) 0%,var(--re-data-grid-skeleton-shine) 50%,var(--re-data-grid-skeleton-line) 100%);background-size:200% 100%;animation:re-dg-skeleton 1.2s ease-in-out infinite}@keyframes re-dg-skeleton{0%{background-position:200% 0}to{background-position:-200% 0}}@keyframes re-dg-spinner{to{transform:rotate(360deg)}}.re-dg-scrollbar{position:absolute;right:0;top:0;bottom:0;opacity:0;transition:opacity .15s ease-in-out;pointer-events:none;z-index:4}.re-dg-scrollbar.visible{opacity:1}.re-dg-scrollbar-thumb{position:absolute;right:var(--re-data-grid-scrollbar-offset);width:var(--re-data-grid-scrollbar-thumb-size);border-radius:var(--re-data-grid-scrollbar-thumb-rounded);background:var(--re-data-grid-scrollbar-thumb-color);pointer-events:auto;-webkit-user-select:none;user-select:none}.re-dg-spacer{width:1px}.re-dg-top{top:0}.re-dg-bottom{bottom:0}\n"], dependencies: [{ kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "component", type: DataGridCellComponent, selector: "re-data-grid-cell", inputs: ["index", "item", "column"] }, { kind: "component", type: SortIcon, selector: "re-sort-ic", inputs: ["direction"] }, { kind: "component", type: ExpandIcon, selector: "re-expand-ic", inputs: ["expanded"] }, { kind: "component", type: CheckboxIcon, selector: "re-checkbox-ic", inputs: ["state", "disabled"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
1823
2513
  }
1824
2514
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.1", ngImport: i0, type: DataGrid, decorators: [{
1825
2515
  type: Component,
1826
- args: [{ selector: 're-data-grid', imports: [NgTemplateOutlet, DataGridCellComponent, SortIcon, ExpandIcon, CheckboxIcon], providers: [DataGridVm], template: "@let items = data();\n@let empty = !loading() && !items?.length;\n@let notEmpty = !!items?.length;\n\n@let pinnedTopH = vm.pinnedTop().length * rowHeight();\n@let pinnedBottomH = vm.pinnedBottom().length * rowHeight();\n\n<div\n #root\n class=\"re-dg-root\"\n [class.loading]=\"loading()\"\n [style.height]=\"styleHeight()\"\n role=\"table\"\n>\n @if (loading()) {\n <div class=\"re-dg-loader\">\n @let loadingTemplate = loadingTpl();\n\n @if (loadingTemplate?.tpl) {\n <ng-container [ngTemplateOutlet]=\"loadingTemplate!.tpl\" />\n } @else {\n <span class=\"re-dg-loader-text\">\u0417\u0430\u0433\u0440\u0443\u0437\u043A\u0430\u2026</span>\n }\n </div>\n }\n\n <div\n #scroll\n class=\"re-dg-body\"\n role=\"rowgroup\"\n (mouseenter)=\"showScrollbar()\"\n (mouseleave)=\"hideScrollbarSoon()\"\n >\n <div class=\"re-dg-header\" role=\"rowgroup\">\n <div #header class=\"re-dg-row re-dg-header-row\" role=\"row\">\n @for (col of vm.columnsToShow(); track col.key) {\n <div\n class=\"re-dg-header-cell\"\n role=\"columnheader\"\n [class.sortable]=\"!!col.sortKey\"\n [class.active-sort]=\"currentSortField && (currentSortField === col.sortKey)\"\n [class.sticky-left]=\"vm.isStickyLeft(col.key)\"\n [class.sticky-right]=\"vm.isStickyRight(col.key)\"\n [style.left.px]=\"vm.stickyOffset(col.key, 'left')\"\n [style.right.px]=\"vm.stickyOffset(col.key, 'right')\"\n [style.width.px]=\"vm.widthByKey(col.key)\"\n [style.min-width.px]=\"col.minWidth || null\"\n [style.max-width.px]=\"col.maxWidth || null\"\n [style.justify-content]=\"col.align || 'left'\"\n [attr.aria-sort]=\"ariaSort(col)\"\n [attr.tabindex]=\"col.sortKey ? 0 : -1\"\n [title]=\"col.header\"\n (click)=\"col.sortKey && onSort(col)\"\n (keydown.enter)=\"col.sortKey && onSort(col)\"\n >\n @if (col.headerTemplate) {\n <ng-container\n [ngTemplateOutlet]=\"col.headerTemplate\"\n [ngTemplateOutletContext]=\"{ $implicit: col.header }\"\n />\n } @else {\n @let isCheckbox = 'type' in col && col.type === 'checkbox';\n @let isMultiSelect = selection().mode === 'multi';\n\n @if (isCheckbox && isMultiSelect) {\n <re-checkbox-ic [state]=\"selector.isAllSelected()\" (click)=\"selector.selectAll()\" />\n } @else {\n <span class=\"re-dg-header-text\">{{ col.header }}</span>\n }\n\n @if (col.sortKey) {\n <span class=\"re-dg-sort-ind\">\n @let direction = currentSortField === col.sortKey ? currentSortOrder : undefined;\n\n @if (sortTpl()) {\n <ng-container\n [ngTemplateOutlet]=\"sortTpl()!.tpl\"\n [ngTemplateOutletContext]=\"{ $implicit: direction }\"\n />\n } @else {\n <re-sort-ic [direction]=\"direction\" />\n }\n </span>\n }\n\n @if (isExpandable(col)) {\n <button (click)=\"$event.stopPropagation(); onExpand(col)\">\n @let expanded = expanderMap().get(col.key);\n\n @if (expanderTpl()) {\n <ng-container\n [ngTemplateOutlet]=\"expanderTpl()!.tpl\"\n [ngTemplateOutletContext]=\"{ $implicit: expanded }\" />\n } @else {\n <re-expand-ic [expanded]=\"expanded\" />\n }\n </button>\n }\n }\n </div>\n }\n </div>\n\n <!-- PINNED TOP ROWS -->\n @if (notEmpty) {\n @for (pr of vm.pinnedTop(); track trackPinnedRow(pr)) {\n <div class=\"re-dg-row re-dg-pinned re-dg-top\" role=\"row\">\n @if (pr.rowTemplate) {\n <ng-container\n [ngTemplateOutlet]=\"pr.rowTemplate!\"\n [ngTemplateOutletContext]=\"{ $implicit: resolvePinnedData(pr) }\"\n />\n } @else {\n @for (col of vm.columnsToShow(); track col.key) {\n <div\n class=\"re-dg-cell\"\n role=\"cell\"\n [class.sticky-left]=\"vm.isStickyLeft(col.key)\"\n [class.sticky-right]=\"vm.isStickyRight(col.key)\"\n [style.left.px]=\"vm.stickyOffset(col.key, 'left')\"\n [style.right.px]=\"vm.stickyOffset(col.key, 'right')\"\n [style.justify-items]=\"col.align || 'left'\"\n [style.height.px]=\"rowHeight()\"\n [style.width.px]=\"vm.widthByKey(col.key)\"\n >\n {{ resolvePinnedData(pr)[col.key] ?? '' }}\n </div>\n }\n }\n </div>\n }\n }\n </div>\n\n @if (empty) {\n @let emptyTemplate = emptyTpl()?.tpl;\n\n <div class=\"re-dg-empty\">\n @if (emptyTemplate) {\n <ng-container [ngTemplateOutlet]=\"emptyTemplate\" />\n } @else {\n <span class=\"re-dg-empty-text\">\u041D\u0435\u0442 \u0434\u0430\u043D\u043D\u044B\u0445</span>\n }\n </div>\n }\n\n <!-- Content -->\n @if (notEmpty) {\n <div class=\"re-dg-spacer\" [style.height.px]=\"items.length * rowHeight() - pinnedBottomH\"></div>\n\n @for (row of visibleRows(); track trackByRow(row); let vi = $index) {\n <div\n class=\"re-dg-row re-dg-data-row\"\n role=\"row\"\n [style.height.px]=\"rowHeight()\"\n [style.top.px]=\"(startIndex + vi) * rowHeight() + pinnedTopH + headerHeight()\"\n [attr.tabindex]=\"0\"\n (click)=\"$event.stopPropagation(); rowClick.emit({ row, index: startIndex + vi })\"\n (keydown.enter)=\"$event.stopPropagation(); rowClick.emit({ row, index: startIndex + vi })\"\n >\n @for (col of vm.columnsToShow(); track col.key) {\n <div\n class=\"re-dg-cell\"\n role=\"cell\"\n [class.expanded]=\"!!col.expandBy\"\n [class]=\"cellClass(col, row)\"\n [class.sticky-left]=\"vm.isStickyLeft(col.key)\"\n [class.sticky-right]=\"vm.isStickyRight(col.key)\"\n [style.left.px]=\"vm.stickyOffset(col.key, 'left')\"\n [style.right.px]=\"vm.stickyOffset(col.key, 'right')\"\n [style.justify-items]=\"col.align || 'left'\"\n [style.text-align]=\"col.align || 'left'\"\n [style.width.px]=\"vm.widthByKey(col.key)\"\n [attr.tabindex]=\"0\"\n (click)=\"onCellClick(row, col, startIndex + vi);\"\n (keydown.enter)=\"onCellClick(row, col, startIndex + vi)\"\n >\n @let isCheckbox = 'type' in col && col.type === 'checkbox';\n\n @if (isCheckbox) {\n <re-checkbox-ic [state]=\"selector.isSelected(row)\" />\n } @else {\n <re-data-grid-cell [index]=\"startIndex + vi\" [item]=\"row\" [column]=\"col\" />\n }\n </div>\n }\n </div>\n }\n }\n\n <!-- PINNED BOTTOM ROWS -->\n @if (notEmpty) {\n <div class=\"re-dg-footer\" role=\"rowgroup\">\n @for (pr of vm.pinnedBottom(); track trackPinnedRow(pr)) {\n <div class=\"re-dg-row re-dg-pinned re-dg-bottom\" role=\"row\">\n @if (pr.rowTemplate) {\n <ng-container\n [ngTemplateOutlet]=\"pr.rowTemplate!\"\n [ngTemplateOutletContext]=\"{ $implicit: resolvePinnedData(pr) }\"\n />\n } @else {\n @for (col of vm.columnsToShow(); track col.key) {\n <div\n class=\"re-dg-cell\"\n role=\"cell\"\n [class.sticky-left]=\"vm.isStickyLeft(col.key)\"\n [class.sticky-right]=\"vm.isStickyRight(col.key)\"\n [style.left.px]=\"vm.stickyOffset(col.key, 'left')\"\n [style.right.px]=\"vm.stickyOffset(col.key, 'right')\"\n [style.justify-items]=\"col.align || 'left'\"\n [style.height.px]=\"rowHeight()\"\n [style.width.px]=\"vm.widthByKey(col.key)\"\n >\n {{ resolvePinnedData(pr)[col.key] ?? '' }}\n </div>\n }\n }\n </div>\n }\n </div>\n }\n </div>\n\n <!-- Overlay scrollbar -->\n <div class=\"re-dg-scrollbar\" [class.visible]=\"vm.scrollbarVisible()\">\n <div\n class=\"re-dg-scrollbar-thumb\"\n role=\"scrollbar\"\n aria-orientation=\"vertical\"\n aria-hidden=\"false\"\n [style.height.px]=\"vm.thumbHeightPx()\"\n [style.transform]=\"'translateY(' + vm.thumbTopPx() + 'px)'\"\n (mousedown)=\"onThumbDown($event)\"\n ></div>\n </div>\n</div>\n\n", styles: [":host{--re-data-grid-min-height: 200px;--re-data-grid-height: 400px;--re-data-grid-rounded: var(--radius-md, 6px);--re-data-grid-separator-color: var(--border-color);--re-data-grid-separator: 1px solid var(--re-data-grid-separator-color);--re-data-grid-surface: var(--surface-primary, #fff);--re-data-grid-active: var(--primary-color, #2a90f4);--re-data-grid-empty-color: #777;--re-data-grid-empty-surface: transparent;--re-data-grid-loading-color: #444;--re-data-grid-loading-surface: rgba(255, 255, 255, .5);--re-data-grid-scrollbar-size: 10px;--re-data-grid-scrollbar-offset: 2px;--re-data-grid-scrollbar-thumb-size: 8px;--re-data-grid-scrollbar-thumb-color: rgba(0, 0, 0, .35);--re-data-grid-scrollbar-thumb-rounded: 4px;--re-data-grid-header-height: 40px;--re-data-grid-header-separator-color: #ccc;--re-data-grid-header-separator: 1px solid var(--re-data-grid-header-separator-color);--re-data-grid-header-surface: #fff;--re-data-grid-header-cell-font-weight: 600;--re-data-grid-header-cell-font-size: .8rem;--re-data-grid-header-cell-color: #000;--re-data-grid-header-cell-surface: #fafafa;--re-data-grid-footer-separator-color: #ccc;--re-data-grid-footer-separator: 1px solid var(--re-data-grid-footer-separator-color);--re-data-grid-footer-surface: #fff;--re-data-grid-row-separator-color: #bbb;--re-data-grid-row-separator: 1px solid var(--re-data-grid-row-separator-color);--re-data-grid-row-odd-surface: var(--re-data-grid-cell-surface);--re-data-grid-column-separator-color: transparent;--re-data-grid-column-separator: 1px solid var(--re-data-grid-column-separator-color);--re-data-grid-column-odd-surface: var(--re-data-grid-cell-surface);--re-data-grid-cell-paddings: .4rem .625rem;--re-data-grid-cell-font-weight: 400;--re-data-grid-cell-font-size: .75rem;--re-data-grid-cell-color: #000;--re-data-grid-cell-surface: #fff;--re-data-grid-sticky-header-cell-surface: #fff;--re-data-grid-sticky-cell-surface: #fdfdfd;--re-data-grid-sticky-cell-left-shadow: 2px 0 2px rgba(0, 0, 0, .03);--re-data-grid-sticky-cell-right-shadow: -2px 0 2px rgba(0, 0, 0, .03);--re-data-grid-pinned-surface: #fcfcfc;--re-data-grid-pinned-separator-color: #eee;--re-data-grid-pinned-separator: 1px solid var(--re-data-grid-pinned-separator-color);--re-data-grid-expander-color: var(--primary-color, currentColor);--re-data-grid-expanded-color: var(--re-data-grid-cell-color, #000);--re-data-grid-expanded-surface: var(--re-data-grid-cell-surface, #fff);display:block;min-height:0}:host,:host *,:host *:before,:host *:after{box-sizing:border-box;outline:none}:host button{outline:none}.re-dg-root{position:relative;display:flex;flex-direction:column;width:100%;min-height:var(--re-data-grid-min-height);border-radius:var(--re-data-grid-rounded);border:var(--re-data-grid-separator)}.re-dg-root.fill{display:block}.re-dg-root.loading{pointer-events:none;-webkit-user-select:none;user-select:none;cursor:wait}.re-dg-root.loading .re-dg-body{overflow:hidden}.re-dg-root.loading .re-dg-scrollbar{display:none!important}.re-dg-root.loading .re-dg-loader{pointer-events:all}.re-dg-body{position:relative;flex:1 1 auto;min-height:0;min-width:0;height:inherit;border:var(--re-data-grid-separator);border-radius:var(--re-data-grid-rounded);background-color:var(--re-data-grid-surface);overflow:auto;scrollbar-width:none;-ms-overflow-style:none}.re-dg-body::-webkit-scrollbar{width:0;height:0}.re-dg-header,.re-dg-footer{position:sticky;z-index:3}.re-dg-header{top:0;background-color:var(--re-data-grid-header-surface)}.re-dg-header-row{min-height:var(--re-data-grid-header-height)}.re-dg-footer{bottom:0;border-radius:0 0 var(--re-data-grid-rounded) var(--re-data-grid-rounded);background-color:var(--re-data-grid-footer-surface)}.re-dg-row{position:relative;display:flex}.re-dg-data-row{position:absolute;left:0;right:0;cursor:default}.re-dg-cell,.re-dg-header-cell{display:flex;flex:0 0 auto;align-items:center;padding:var(--re-data-grid-cell-paddings);border-right:var(--re-data-grid-column-separator);text-overflow:ellipsis;white-space:nowrap;overflow:hidden}.re-dg-cell{width:100%;border-bottom:var(--re-data-grid-row-separator);font-weight:var(--re-data-grid-cell-font-weight);font-size:var(--re-data-grid-cell-font-size);color:var(--re-data-grid-cell-color);background-color:var(--re-data-grid-cell-surface)}.re-dg-row:nth-child(odd) .re-dg-cell{background-color:var(--re-data-grid-row-odd-surface)}.re-dg-cell:nth-child(odd){background-color:var(--re-data-grid-column-odd-surface)}.re-dg-bottom>.re-dg-cell{border-top:var(--re-data-grid-footer-separator)}.re-dg-header-cell{align-items:center;gap:.75rem;border-bottom:var(--re-data-grid-header-separator);font-weight:var(--re-data-grid-header-cell-font-weight);font-size:var(--re-data-grid-header-cell-font-size);color:var(--re-data-grid-header-cell-color);background:var(--re-data-grid-header-cell-surface);-webkit-user-select:none;user-select:none;transition:color .3s ease-in-out}.re-dg-header-cell:first-child{border-radius:var(--re-data-grid-rounded) 0 0 0}.re-dg-header-cell:last-child{border-radius:0 var(--re-data-grid-rounded) 0 0}.re-dg-data-row:last-child .re-dg-cell:first-child{border-radius:0 0 0 var(--re-data-grid-rounded)}.re-dg-data-row:last-child .re-dg-cell:last-child{border-radius:0 0 var(--re-data-grid-rounded) 0}.re-dg-header-cell .re-dg-header-text{display:-webkit-box;max-height:2.4em;line-height:1.2;white-space:normal;-webkit-box-orient:vertical;-webkit-line-clamp:2;overflow:hidden}.re-dg-row.re-dg-pinned>.re-dg-cell{border-bottom:var(--re-data-grid-pinned-separator);background-color:var(--re-data-grid-pinned-surface)}.re-dg-row .re-dg-header-cell.sticky-left{box-shadow:var(--re-data-grid-sticky-cell-left-shadow);background-color:var(--re-data-grid-sticky-header-cell-surface)}.re-dg-row .re-dg-cell.sticky-left{box-shadow:var(--re-data-grid-sticky-cell-left-shadow);background-color:var(--re-data-grid-sticky-cell-surface)}.re-dg-row .re-dg-header-cell.sticky-right{box-shadow:var(--re-data-grid-sticky-cell-right-shadow);background-color:var(--re-data-grid-sticky-header-cell-surface)}.re-dg-row .re-dg-cell.sticky-right{box-shadow:var(--re-data-grid-sticky-cell-right-shadow);background-color:var(--re-data-grid-sticky-cell-surface)}.sticky-left,.sticky-right{position:sticky;z-index:2}.sortable{cursor:pointer}.active-sort{color:var(--re-data-grid-active)}.re-dg-sort-ind{margin-left:6px}.re-dg-data-row .re-dg-cell.expanded{color:var(--re-data-grid-expanded-color);background:var(--re-data-grid-expanded-surface)}.re-dg-empty{position:absolute;inset:0;display:grid;place-items:center;height:inherit;width:100%;border-radius:var(--re-data-grid-rounded);color:var(--re-data-grid-empty-color);background:var(--re-data-grid-empty-surface)}.re-dg-empty-text{width:100%;text-align:center}.re-dg-loader{position:absolute;inset:0;display:grid;place-items:center;height:inherit;border-radius:var(--re-data-grid-rounded);background-color:var(--re-data-grid-loading-surface);color:var(--re-data-grid-loading-color);z-index:5}.re-dg-scrollbar{position:absolute;right:0;top:0;bottom:0;opacity:0;transition:opacity .15s ease-in-out;pointer-events:none;z-index:4}.re-dg-scrollbar.visible{opacity:1}.re-dg-scrollbar-thumb{position:absolute;right:var(--re-data-grid-scrollbar-offset);width:var(--re-data-grid-scrollbar-thumb-size);border-radius:var(--re-data-grid-scrollbar-thumb-rounded);background:var(--re-data-grid-scrollbar-thumb-color);pointer-events:auto;-webkit-user-select:none;user-select:none}.re-dg-spacer{width:1px}.re-dg-top{top:0}.re-dg-bottom{bottom:0}\n"] }]
1827
- }], ctorParameters: () => [], propDecorators: { data: [{ type: i0.Input, args: [{ isSignal: true, alias: "data", required: false }] }], columns: [{ type: i0.Input, args: [{ isSignal: true, alias: "columns", required: false }] }], mode: [{ type: i0.Input, args: [{ isSignal: true, alias: "mode", required: false }] }], pinnedRows: [{ type: i0.Input, args: [{ isSignal: true, alias: "pinnedRows", required: false }] }], hasIndexColumn: [{ type: i0.Input, args: [{ isSignal: true, alias: "hasIndexColumn", required: false }] }], selection: [{ type: i0.Input, args: [{ isSignal: true, alias: "selection", required: false }] }], pageSize: [{ type: i0.Input, args: [{ isSignal: true, alias: "pageSize", required: false }] }], rowHeight: [{ type: i0.Input, args: [{ isSignal: true, alias: "rowHeight", required: false }] }], height: [{ type: i0.Input, args: [{ isSignal: true, alias: "height", required: false }] }], virtualBuffer: [{ type: i0.Input, args: [{ isSignal: true, alias: "virtualBuffer", required: false }] }], loading: [{ type: i0.Input, args: [{ isSignal: true, alias: "loading", required: false }] }], rowKey: [{ type: i0.Input, args: [{ isSignal: true, alias: "rowKey", required: false }] }], pageStartFromZero: [{ type: i0.Input, args: [{ isSignal: true, alias: "pageStartFromZero", required: false }] }], pageChange: [{ type: i0.Output, args: ["pageChange"] }], sortChange: [{ type: i0.Output, args: ["sortChange"] }], selectChange: [{ type: i0.Output, args: ["selectChange"] }], rowClick: [{ type: i0.Output, args: ["rowClick"] }], cellClick: [{ type: i0.Output, args: ["cellClick"] }], rootEl: [{ type: i0.ViewChild, args: ['root', { isSignal: true }] }], scrollEl: [{ type: i0.ViewChild, args: ['scroll', { isSignal: true }] }], headerEl: [{ type: i0.ViewChild, args: ['header', { isSignal: true }] }], cellTypedSlotRefs: [{ type: i0.ContentChildren, args: [i0.forwardRef(() => DataGridTypeCellTemplateDirective), { isSignal: true }] }], cellDataSlotRefs: [{ type: i0.ContentChildren, args: [i0.forwardRef(() => DataGridCellTemplateDirective), { isSignal: true }] }], headerSlotRefs: [{ type: i0.ContentChildren, args: [i0.forwardRef(() => DataGridHeaderTemplateDirective), { isSignal: true }] }], emptySlotRefs: [{ type: i0.ContentChildren, args: [i0.forwardRef(() => DataGridCellEmptyDirective), { isSignal: true }] }], loadingSlotRefs: [{ type: i0.ContentChildren, args: [i0.forwardRef(() => DataGridCellLoadingDirective), { isSignal: true }] }], sortIcSlotRefs: [{ type: i0.ContentChildren, args: [i0.forwardRef(() => DataGridSortIconDirective), { isSignal: true }] }], expanderIcSlotRefs: [{ type: i0.ContentChildren, args: [i0.forwardRef(() => DataGridExpanderIconDirective), { isSignal: true }] }] } });
2516
+ args: [{ selector: 're-data-grid', imports: [NgTemplateOutlet, DataGridCellComponent, SortIcon, ExpandIcon, CheckboxIcon], providers: [DataGridVm], changeDetection: ChangeDetectionStrategy.OnPush, template: "@let items = data();\r\n@let empty = !loading() && !items?.length;\r\n@let notEmpty = !!items?.length;\r\n@let skeletonRowsCount = 4;\r\n@let skeletonMode = loadingMode() === 'skeleton';\r\n@let spinnerMode = loadingMode() === 'spinner';\r\n@let showInfinitySkeleton = loading() && skeletonMode && mode() === 'infinity';\r\n@let showPaginationSkeleton = loading() && skeletonMode && mode() === 'pagination' && !notEmpty;\r\n@let showSpinnerLoading = loading() && spinnerMode;\r\n@let extraInfinitySkeletonRows = showInfinitySkeleton ? skeletonRowsCount : 0;\r\n\r\n@let pinnedTopH = vm.pinnedTop().length * rowHeight();\r\n@let pinnedBottomH = vm.pinnedBottom().length * rowHeight();\r\n@let rowH = rowHeight();\r\n@let contentW = vm.contentWidth();\r\n@let cols = vm.columnsToShow();\r\n@let stickyTop = pinnedTopH + headerHeight();\r\n@let normalizedHeaderGroups = vm.normalizedHeaderGroups();\r\n@let stickyRow = stickyRowData();\r\n@let stickyIndex = stickyRowIndex();\r\n\r\n<div\r\n #root\r\n class=\"re-dg-root\"\r\n [class.loading]=\"showSpinnerLoading\"\r\n [style.height]=\"styleHeight()\"\r\n role=\"table\"\r\n>\r\n @if (showSpinnerLoading) {\r\n <div class=\"re-dg-loader\">\r\n @let loadingTemplate = loadingTpl();\r\n\r\n @if (loadingTemplate?.tpl) {\r\n <ng-container [ngTemplateOutlet]=\"loadingTemplate!.tpl\" />\r\n } @else {\r\n <span class=\"re-dg-loader-spinner\" aria-label=\"Loading\"></span>\r\n }\r\n </div>\r\n }\r\n\r\n <div\r\n #scroll\r\n class=\"re-dg-body\"\r\n role=\"rowgroup\"\r\n (mouseenter)=\"showScrollbar()\"\r\n (mouseleave)=\"hideScrollbarSoon()\"\r\n >\r\n <div class=\"re-dg-header\" role=\"rowgroup\">\r\n <div #header class=\"re-dg-header-rows\">\r\n @if (normalizedHeaderGroups.length) {\r\n <div class=\"re-dg-row re-dg-header-group-row\" role=\"row\" [style.width.px]=\"vm.contentWidth()\" [style.min-width.%]=\"100\">\r\n @for (group of normalizedHeaderGroups; track group.key) {\r\n @let groupStickyLeft = !!group.startKey && !!group.endKey && vm.isStickyLeft(group.startKey) && vm.isStickyLeft(group.endKey);\r\n @let groupStickyRight = !!group.startKey && !!group.endKey && vm.isStickyRight(group.startKey) && vm.isStickyRight(group.endKey);\r\n\r\n <div\r\n class=\"re-dg-header-cell re-dg-header-group-cell\"\r\n role=\"columnheader\"\r\n [class.sticky-left]=\"groupStickyLeft\"\r\n [class.sticky-right]=\"groupStickyRight\"\r\n [style.left.px]=\"groupStickyLeft && group.startKey ? vm.stickyOffset(group.startKey, 'left') : null\"\r\n [style.right.px]=\"groupStickyRight && group.endKey ? vm.stickyOffset(group.endKey, 'right') : null\"\r\n [style.width.px]=\"group.widthPx\"\r\n [style.justify-content]=\"group.align || 'left'\"\r\n [title]=\"group.title || ''\"\r\n >\r\n @if (group.titleTemplate) {\r\n <ng-container\r\n [ngTemplateOutlet]=\"group.titleTemplate\"\r\n [ngTemplateOutletContext]=\"{ $implicit: group.title || '' }\"\r\n />\r\n } @else {\r\n <span class=\"re-dg-header-text\">{{ group.title || '' }}</span>\r\n }\r\n </div>\r\n }\r\n </div>\r\n }\r\n\r\n <div class=\"re-dg-row re-dg-header-row\" role=\"row\" [style.width.px]=\"vm.contentWidth()\" [style.min-width.%]=\"100\">\r\n @for (col of vm.columnsToShow(); track col.key) {\r\n <div\r\n class=\"re-dg-header-cell\"\r\n role=\"columnheader\"\r\n [class.sortable]=\"!!col.sortKey\"\r\n [class.active-sort]=\"currentSortField && (currentSortField === col.sortKey)\"\r\n [class.sticky-left]=\"vm.isStickyLeft(col.key)\"\r\n [class.sticky-right]=\"vm.isStickyRight(col.key)\"\r\n [style.left.px]=\"vm.stickyOffset(col.key, 'left')\"\r\n [style.right.px]=\"vm.stickyOffset(col.key, 'right')\"\r\n [style.width.px]=\"vm.widthByKey(col.key)\"\r\n [style.min-width.px]=\"col.minWidth || null\"\r\n [style.max-width.px]=\"col.maxWidth || null\"\r\n [style.justify-content]=\"col.align || 'left'\"\r\n [attr.aria-sort]=\"ariaSort(col)\"\r\n [attr.tabindex]=\"col.sortKey ? 0 : -1\"\r\n [title]=\"col.header\"\r\n (click)=\"col.sortKey && onSort(col)\"\r\n (keydown.enter)=\"col.sortKey && onSort(col)\"\r\n >\r\n @let isCheckbox = 'type' in col && col.type === 'checkbox';\r\n @let isMultiSelect = selection().mode === 'multi';\r\n\r\n @if (isCheckbox && isMultiSelect) {\r\n <re-checkbox-ic [state]=\"selector.isAllSelected()\" (click)=\"selector.selectAll()\" />\r\n } @else {\r\n @if (col.headerTemplate) {\r\n <ng-container\r\n [ngTemplateOutlet]=\"col.headerTemplate\"\r\n [ngTemplateOutletContext]=\"{ $implicit: col.header }\"\r\n />\r\n } @else {\r\n <span class=\"re-dg-header-text\">{{ col.header }}</span>\r\n }\r\n }\r\n\r\n @if (col.sortKey) {\r\n <span class=\"re-dg-sort-ind\">\r\n @let direction = currentSortField === col.sortKey ? currentSortOrder : undefined;\r\n\r\n @if (sortTpl()) {\r\n <ng-container\r\n [ngTemplateOutlet]=\"sortTpl()!.tpl\"\r\n [ngTemplateOutletContext]=\"{ $implicit: direction }\"\r\n />\r\n } @else {\r\n <re-sort-ic [direction]=\"direction\" />\r\n }\r\n </span>\r\n }\r\n\r\n @if (isExpandable(col)) {\r\n <button (click)=\"$event.stopPropagation(); onExpand(col)\">\r\n @let expanded = expanderMap().get(col.key);\r\n\r\n @if (expanderTpl()) {\r\n <ng-container\r\n [ngTemplateOutlet]=\"expanderTpl()!.tpl\"\r\n [ngTemplateOutletContext]=\"{ $implicit: expanded }\" />\r\n } @else {\r\n <re-expand-ic [expanded]=\"expanded\" />\r\n }\r\n </button>\r\n }\r\n </div>\r\n }\r\n </div>\r\n </div>\r\n\r\n <!-- PINNED TOP ROWS -->\r\n @if (notEmpty) {\r\n @for (pr of vm.pinnedTop(); track trackPinnedRow(pr)) {\r\n <div class=\"re-dg-row re-dg-pinned re-dg-top\" role=\"row\" [style.width.px]=\"contentW\" [style.min-width.%]=\"100\">\r\n @if (pr.rowTemplate) {\r\n <ng-container\r\n [ngTemplateOutlet]=\"pr.rowTemplate!\"\r\n [ngTemplateOutletContext]=\"{ $implicit: resolvePinnedData(pr) }\"\r\n />\r\n } @else {\r\n @for (col of cols; track col.key) {\r\n @let stickyLeft = vm.stickyOffset(col.key, 'left');\r\n @let stickyRight = vm.stickyOffset(col.key, 'right');\r\n @let isLeft = vm.isStickyLeft(col.key);\r\n @let isRight = vm.isStickyRight(col.key);\r\n <div\n class=\"re-dg-cell\"\n role=\"cell\"\n [class.sticky-left]=\"isLeft\"\n [class.sticky-right]=\"isRight\"\n [style.left.px]=\"stickyLeft\"\n [style.right.px]=\"stickyRight\"\n [style.justify-items]=\"col.align || 'left'\"\n [style.height.px]=\"rowH\"\n [style.width.px]=\"vm.widthByKey(col.key)\"\n (mouseenter)=\"showTooltip($event, $any(resolvePinnedData(pr)), col, -1)\"\n (mouseleave)=\"hideTooltip()\"\n >\n {{ resolvePinnedData(pr)[col.key] ?? '' }}\n </div>\n }\r\n }\r\n </div>\r\n }\r\n }\r\n </div>\r\n\r\n <!-- STICKY ROW -->\r\n @if (stickyRow && stickyIndex !== null) {\r\n <div\r\n class=\"re-dg-row re-dg-data-row re-dg-sticky-row\"\r\n role=\"row\"\r\n [style.width.px]=\"vm.contentWidth()\"\r\n [style.min-width.%]=\"100\"\r\n [style.height.px]=\"rowHeight()\"\r\n [style.top.px]=\"stickyRowTopPx()\"\r\n [attr.tabindex]=\"0\"\r\n (click)=\"$event.stopPropagation(); rowClick.emit({ row: stickyRow, index: stickyIndex, event: $event })\"\r\n (contextmenu)=\"$event.stopPropagation(); rowContext.emit({ row: stickyRow, index: stickyIndex, event: $event })\"\r\n (dblclick)=\"$event.stopPropagation(); rowDoubleClick.emit({ row: stickyRow, index: stickyIndex, event: $event })\"\r\n (keydown.enter)=\"$event.stopPropagation(); rowClick.emit({ row: stickyRow, index: stickyIndex, event: $event })\"\r\n >\r\n @let stickyTemplate = stickyRowTpl();\r\n @let rowTemplate = resolveRowTemplate(stickyRow, stickyIndex);\r\n\r\n @if (stickyTemplate?.tpl) {\r\n <ng-container\r\n [ngTemplateOutlet]=\"stickyTemplate!.tpl\"\r\n [ngTemplateOutletContext]=\"{ $implicit: stickyRow, index: stickyIndex }\"\r\n />\r\n } @else if (rowTemplate) {\r\n <ng-container\r\n [ngTemplateOutlet]=\"rowTemplate\"\r\n [ngTemplateOutletContext]=\"{\r\n $implicit: stickyRow,\r\n index: stickyIndex,\r\n columns: vm.columnsToShow(),\r\n rowHeight: rowHeight(),\r\n isSticky: true\r\n }\"\r\n />\r\n } @else {\r\n @for (col of vm.columnsToShow(); track col.key) {\r\n <div\n class=\"re-dg-cell\"\n role=\"cell\"\n [class.expanded]=\"!!col.expandBy\"\n [class]=\"cellClass(col, stickyRow)\"\n [class.sticky-left]=\"vm.isStickyLeft(col.key)\"\n [class.sticky-right]=\"vm.isStickyRight(col.key)\"\n [style.left.px]=\"vm.stickyOffset(col.key, 'left')\"\n [style.right.px]=\"vm.stickyOffset(col.key, 'right')\"\n [style.justify-items]=\"col.align || 'left'\"\n [style.text-align]=\"col.align || 'left'\"\n [style.width.px]=\"vm.widthByKey(col.key)\"\n [attr.tabindex]=\"0\"\n (mouseenter)=\"showTooltip($event, stickyRow, col, stickyIndex)\"\n (mouseleave)=\"hideTooltip()\"\n (click)=\"onCellClick(stickyRow, col, stickyIndex, $event);\"\n (contextmenu)=\"onCellContext(stickyRow, col, stickyIndex, $event)\"\n (dblclick)=\"onCellDoubleClick(stickyRow, col, stickyIndex, $event)\"\n (keydown.enter)=\"onCellClick(stickyRow, col, stickyIndex, $event)\"\n >\n @let isCheckbox = 'type' in col && col.type === 'checkbox';\r\n\r\n @if (isCheckbox) {\r\n <re-checkbox-ic [state]=\"selector.isSelected(stickyRow)\" />\r\n } @else {\r\n <re-data-grid-cell [index]=\"stickyIndex\" [item]=\"stickyRow\" [column]=\"col\" />\r\n }\r\n </div>\r\n }\r\n }\r\n </div>\r\n }\r\n\r\n @if (empty) {\r\n @let emptyTemplate = emptyTpl()?.tpl;\r\n\r\n <div class=\"re-dg-empty\">\r\n @if (emptyTemplate) {\r\n <ng-container [ngTemplateOutlet]=\"emptyTemplate\" />\r\n } @else {\r\n <span class=\"re-dg-empty-text\">{{ defaults.translations.emptyState }}</span>\r\n }\r\n </div>\r\n }\r\n\r\n <!-- Content -->\r\n @if (notEmpty) {\r\n <div\r\n class=\"re-dg-spacer\"\r\n [style.width.px]=\"contentW\"\r\n [style.height.px]=\"(items.length + extraInfinitySkeletonRows) * rowH - pinnedBottomH\"></div>\r\n\r\n @for (slot of renderSlots(); track slot) {\r\n @let rowIndex = startIndex + slot;\r\n @let row = items[rowIndex];\r\n @let rowTemplate = row ? resolveRowTemplate(row, rowIndex) : null;\r\n\r\n @if (row && !isStickyRowIndex(rowIndex)) {\r\n <div\r\n class=\"re-dg-row re-dg-data-row\"\r\n role=\"row\"\r\n [style.width.px]=\"contentW\"\r\n [style.min-width.%]=\"100\"\r\n [style.height.px]=\"rowH\"\r\n [style.transform]=\"'translateY(' + (rowIndex * rowH + stickyTop) + 'px)'\"\r\n [attr.tabindex]=\"0\"\r\n (click)=\"$event.stopPropagation(); rowClick.emit({ row, index: rowIndex, event: $event })\"\r\n (contextmenu)=\"$event.stopPropagation(); rowContext.emit({ row, index: rowIndex, event: $event })\"\r\n (dblclick)=\"$event.stopPropagation(); rowDoubleClick.emit({ row, index: rowIndex, event: $event })\"\r\n (keydown.enter)=\"$event.stopPropagation(); rowClick.emit({ row, index: rowIndex, event: $event })\"\r\n >\r\n @if (rowTemplate) {\r\n <ng-container\r\n [ngTemplateOutlet]=\"rowTemplate\"\r\n [ngTemplateOutletContext]=\"{\r\n $implicit: row,\r\n index: rowIndex,\r\n columns: vm.columnsToShow(),\r\n rowHeight: rowHeight(),\r\n isSticky: false\r\n }\"\r\n />\r\n } @else {\r\n @for (col of cols; track col.key) {\r\n @let stickyLeft = vm.stickyOffset(col.key, 'left');\r\n @let stickyRight = vm.stickyOffset(col.key, 'right');\r\n @let isLeft = vm.isStickyLeft(col.key);\r\n @let isRight = vm.isStickyRight(col.key);\r\n <div\n class=\"re-dg-cell\"\n role=\"cell\"\n [class.expanded]=\"!!col.expandBy\"\n [class]=\"cellClass(col, row)\"\n [class.sticky-left]=\"isLeft\"\n [class.sticky-right]=\"isRight\"\n [style.left.px]=\"stickyLeft\"\n [style.right.px]=\"stickyRight\"\n [style.justify-items]=\"col.align || 'left'\"\n [style.text-align]=\"col.align || 'left'\"\n [style.width.px]=\"vm.widthByKey(col.key)\"\n [attr.tabindex]=\"0\"\n (mouseenter)=\"showTooltip($event, row, col, rowIndex)\"\n (mouseleave)=\"hideTooltip()\"\n (click)=\"onCellClick(row, col, rowIndex, $event);\"\n (contextmenu)=\"onCellContext(row, col, rowIndex, $event)\"\n (dblclick)=\"onCellDoubleClick(row, col, rowIndex, $event)\"\n (keydown.enter)=\"onCellClick(row, col, rowIndex, $event)\"\n >\n @let isCheckbox = 'type' in col && col.type === 'checkbox';\r\n\r\n @if (isCheckbox) {\r\n <re-checkbox-ic [state]=\"selector.isSelected(row)\" />\r\n } @else {\r\n <re-data-grid-cell [index]=\"rowIndex\" [item]=\"row\" [column]=\"col\" />\r\n }\r\n </div>\r\n }\r\n }\r\n </div>\r\n }\r\n }\r\n }\r\n\r\n @if (showInfinitySkeleton || showPaginationSkeleton) {\r\n @let loadingTemplate = loadingTpl();\r\n\r\n @if (loadingTemplate?.tpl) {\r\n <ng-container [ngTemplateOutlet]=\"loadingTemplate!.tpl\" />\r\n } @else {\r\n @for (si of [0, 1, 2, 3]; track si) {\r\n <div\r\n class=\"re-dg-row re-dg-data-row re-dg-skeleton-row\"\r\n role=\"row\"\r\n [style.width.px]=\"contentW\"\r\n [style.min-width.%]=\"100\"\r\n [style.height.px]=\"rowH\"\r\n [style.transform]=\"'translateY(' + (((showInfinitySkeleton ? items.length : 0) + si) * rowH + stickyTop) + 'px)'\"\r\n >\r\n <span class=\"re-dg-skeleton-row-line\"></span>\r\n </div>\r\n }\r\n }\r\n }\r\n\r\n <!-- PINNED BOTTOM ROWS -->\r\n @if (notEmpty) {\r\n <div class=\"re-dg-footer\" role=\"rowgroup\">\r\n @for (pr of vm.pinnedBottom(); track trackPinnedRow(pr)) {\r\n <div class=\"re-dg-row re-dg-pinned re-dg-bottom\" role=\"row\" [style.width.px]=\"contentW\" [style.min-width.%]=\"100\">\r\n @if (pr.rowTemplate) {\r\n <ng-container\r\n [ngTemplateOutlet]=\"pr.rowTemplate!\"\r\n [ngTemplateOutletContext]=\"{ $implicit: resolvePinnedData(pr) }\"\r\n />\r\n } @else {\r\n @for (col of cols; track col.key) {\r\n @let stickyLeft = vm.stickyOffset(col.key, 'left');\r\n @let stickyRight = vm.stickyOffset(col.key, 'right');\r\n @let isLeft = vm.isStickyLeft(col.key);\r\n @let isRight = vm.isStickyRight(col.key);\r\n <div\n class=\"re-dg-cell\"\n role=\"cell\"\n [class.sticky-left]=\"isLeft\"\n [class.sticky-right]=\"isRight\"\n [style.left.px]=\"stickyLeft\"\n [style.right.px]=\"stickyRight\"\n [style.justify-items]=\"col.align || 'left'\"\n [style.height.px]=\"rowH\"\n [style.width.px]=\"vm.widthByKey(col.key)\"\n (mouseenter)=\"showTooltip($event, $any(resolvePinnedData(pr)), col, -1)\"\n (mouseleave)=\"hideTooltip()\"\n >\n {{ resolvePinnedData(pr)[col.key] ?? '' }}\n </div>\n }\r\n }\r\n </div>\r\n }\r\n </div>\r\n }\r\n </div>\r\n\r\n @let tooltipStateValue = tooltipState();\n <div\n class=\"re-dg-tooltip\"\n #tooltip\n [class.visible]=\"tooltipStateValue.visible\"\n [style.left.px]=\"tooltipStateValue.x\"\n [style.top.px]=\"tooltipStateValue.y\"\n >\n @if (tooltipStateValue.tpl) {\n <ng-container\n [ngTemplateOutlet]=\"tooltipStateValue.tpl\"\n [ngTemplateOutletContext]=\"tooltipStateValue.ctx\"\n />\n } @else {\n {{ tooltipStateValue.text }}\n }\n </div>\n\n <!-- Overlay scrollbar -->\n <div class=\"re-dg-scrollbar\" [class.visible]=\"vm.scrollbarVisible()\">\n <div\r\n class=\"re-dg-scrollbar-thumb\"\r\n role=\"scrollbar\"\r\n aria-orientation=\"vertical\"\r\n aria-hidden=\"false\"\r\n [style.height.px]=\"vm.thumbHeightPx()\"\r\n [style.transform]=\"'translateY(' + vm.thumbTopPx() + 'px)'\"\r\n (mousedown)=\"onThumbDown($event)\"\r\n ></div>\r\n </div>\r\n</div>\r\n", styles: [":host{--re-data-grid-min-height: 200px;--re-data-grid-height: 400px;--re-data-grid-rounded: var(--radius-md, 6px);--re-data-grid-separator-color: var(--border-color);--re-data-grid-separator: 1px solid var(--re-data-grid-separator-color);--re-data-grid-surface: var(--surface-neutral, #fff);--re-data-grid-active: var(--primary-color, #2a90f4);--re-data-grid-empty-color: #777;--re-data-grid-empty-surface: transparent;--re-data-grid-loading-color: #444;--re-data-grid-loading-surface: rgba(255, 255, 255, .5);--re-data-grid-spinner-size: 2rem;--re-data-grid-spinner-width: .25rem;--re-data-grid-spinner-track-color: rgba(0, 0, 0, .12);--re-data-grid-skeleton-width: 100%;--re-data-grid-skeleton-height: 100%;--re-data-grid-skeleton-rounded: var(--re-data-grid-rounded, .75rem);--re-data-grid-skeleton-shine: rgba(255, 255, 255, .8);--re-data-grid-skeleton-line: #e7ebf0;--re-data-grid-scrollbar-size: 4px;--re-data-grid-scrollbar-offset: 2px;--re-data-grid-scrollbar-track-rounded: .25rem;--re-data-grid-scrollbar-track-surface: transparent;--re-data-grid-scrollbar-thumb-size: 8px;--re-data-grid-scrollbar-thumb-color: rgba(0, 0, 0, .25);--re-data-grid-scrollbar-thumb-active-color: rgba(0, 0, 0, .45);--re-data-grid-scrollbar-thumb-rounded: var(--re-data-grid-scrollbar-track-rounded);--re-data-grid-tooltip-surface: #0f172a;--re-data-grid-tooltip-color: #f8fafc;--re-data-grid-tooltip-radius: .5rem;--re-data-grid-tooltip-padding: .4rem .6rem;--re-data-grid-tooltip-shadow: 0 8px 24px rgba(15, 23, 42, .25);--re-data-grid-tooltip-z: 60;--re-data-grid-header-surface: #fff;--re-data-grid-header-group-row-height: var(--re-data-grid-header-row-height);--re-data-grid-header-group-row-separator-color: var(--re-data-grid-header-row-separator-color);--re-data-grid-header-group-row-separator: 1px solid var(--re-data-grid-header-group-row-separator-color);--re-data-grid-header-row-height: 40px;--re-data-grid-header-row-separator-color: #ccc;--re-data-grid-header-row-separator: 1px solid var(--re-data-grid-header-row-separator-color);--re-data-grid-header-group-cell-font-weight: var(--re-data-grid-header-cell-font-weight);--re-data-grid-header-group-cell-font-size: var(--re-data-grid-header-cell-font-size);--re-data-grid-header-group-cell-color: var(--re-data-grid-header-cell-color);--re-data-grid-header-group-cell-surface: var(--re-data-grid-header-cell-surface);--re-data-grid-header-cell-font-weight: 600;--re-data-grid-header-cell-font-size: .8rem;--re-data-grid-header-cell-color: #000;--re-data-grid-header-cell-surface: #fafafa;--re-data-grid-footer-separator-color: #ccc;--re-data-grid-footer-separator: 1px solid var(--re-data-grid-footer-separator-color);--re-data-grid-footer-surface: #fff;--re-data-grid-row-separator-color: #bbb;--re-data-grid-row-separator: 1px solid var(--re-data-grid-row-separator-color);--re-data-grid-row-odd-surface: var(--re-data-grid-cell-surface);--re-data-grid-column-separator-color: transparent;--re-data-grid-column-separator: 1px solid var(--re-data-grid-column-separator-color);--re-data-grid-column-odd-surface: var(--re-data-grid-cell-surface);--re-data-grid-cell-paddings: .4rem .625rem;--re-data-grid-cell-font-weight: 400;--re-data-grid-cell-font-size: .75rem;--re-data-grid-cell-color: #000;--re-data-grid-cell-surface: #fff;--re-data-grid-sticky-header-cell-surface: #fff;--re-data-grid-sticky-cell-surface: #fdfdfd;--re-data-grid-sticky-cell-left-shadow: 2px 0 2px rgba(0, 0, 0, .03);--re-data-grid-sticky-cell-right-shadow: -2px 0 2px rgba(0, 0, 0, .03);--re-data-grid-pinned-surface: #fcfcfc;--re-data-grid-pinned-separator-color: #eee;--re-data-grid-pinned-separator: 1px solid var(--re-data-grid-pinned-separator-color);--re-data-grid-expander-color: var(--primary-color, currentColor);--re-data-grid-expanded-color: var(--re-data-grid-cell-color, #000);--re-data-grid-expanded-surface: var(--re-data-grid-cell-surface, #fff);display:block;min-height:0}:host,:host *,:host *:before,:host *:after{box-sizing:border-box;outline:none}:host button{outline:none}.re-dg-root{position:relative;display:flex;flex-direction:column;width:100%;min-height:var(--re-data-grid-min-height);border-radius:var(--re-data-grid-rounded);border:var(--re-data-grid-separator)}.re-dg-root.fill{display:block}.re-dg-root.loading{pointer-events:none;-webkit-user-select:none;user-select:none;cursor:wait}.re-dg-root.loading .re-dg-body{overflow:hidden}.re-dg-root.loading .re-dg-scrollbar{display:none!important}.re-dg-root.loading .re-dg-loader{pointer-events:all}.re-dg-body{position:relative;flex:1 1 auto;min-height:0;min-width:0;height:inherit;border:var(--re-data-grid-separator);border-radius:var(--re-data-grid-rounded);background-color:var(--re-data-grid-surface);overflow:auto;scrollbar-width:auto;-ms-overflow-style:auto}.re-dg-body::-webkit-scrollbar{width:var(--re-data-grid-scrollbar-size);height:var(--re-data-grid-scrollbar-size)}.re-dg-body::-webkit-scrollbar:vertical{width:0}.re-dg-body::-webkit-scrollbar-track{border-radius:var(--re-data-grid-scrollbar-track-rounded);background:var(--re-data-grid-scrollbar-track-surface)}.re-dg-body::-webkit-scrollbar-thumb{border-radius:var(--re-data-grid-scrollbar-thumb-rounded);background:var(--re-data-grid-scrollbar-thumb-color);transition:opacity .3s ease}.re-dg-body::-webkit-scrollbar-thumb:hover{background:var(--re-data-grid-scrollbar-thumb-active-color)}.re-dg-header,.re-dg-footer{position:sticky;z-index:3}.re-dg-header{top:0;background-color:var(--re-data-grid-header-surface)}.re-dg-header-row{min-height:var(--re-data-grid-header-row-height)}.re-dg-header-group-row{min-height:var(--re-data-grid-header-group-row-height)}.re-dg-header-rows{display:flex;flex-direction:column}.re-dg-footer{bottom:0;border-radius:0 0 var(--re-data-grid-rounded) var(--re-data-grid-rounded);background-color:var(--re-data-grid-footer-surface)}.re-dg-row{position:relative;display:flex}.re-dg-data-row{position:absolute;left:0;top:0;min-width:100%;cursor:default;will-change:transform}.re-dg-sticky-row{z-index:2;top:0}.re-dg-cell,.re-dg-header-cell,.re-dg-pinned-row{display:flex;flex:0 0 auto;align-items:center;padding:var(--re-data-grid-cell-paddings);border-right:var(--re-data-grid-column-separator);text-overflow:ellipsis;white-space:nowrap;overflow:hidden}.re-dg-cell,.re-dg-pinned-row{width:100%;border-bottom:var(--re-data-grid-row-separator);font-weight:var(--re-data-grid-cell-font-weight);font-size:var(--re-data-grid-cell-font-size);color:var(--re-data-grid-cell-color);background-color:var(--re-data-grid-cell-surface)}.re-dg-row:nth-child(odd) .re-dg-cell{background-color:var(--re-data-grid-row-odd-surface)}.re-dg-cell:nth-child(odd){background-color:var(--re-data-grid-column-odd-surface)}.re-dg-bottom>.re-dg-cell{border-top:var(--re-data-grid-footer-separator)}.re-dg-header-cell{align-items:center;gap:.75rem;border-bottom:var(--re-data-grid-header-row-separator);font-weight:var(--re-data-grid-header-cell-font-weight);font-size:var(--re-data-grid-header-cell-font-size);color:var(--re-data-grid-header-cell-color);background:var(--re-data-grid-header-cell-surface);-webkit-user-select:none;user-select:none;transition:color .3s ease-in-out}.re-dg-header-cell.re-dg-header-group-cell{border-bottom:var(--re-data-grid-header-group-row-separator);font-weight:var(--re-data-grid-header-group-cell-font-weight);font-size:var(--re-data-grid-header-group-cell-font-size);color:var(--re-data-grid-header-group-cell-color);background:var(--re-data-grid-header-group-cell-surface)}.re-dg-header-row:first-child .re-dg-header-cell:first-child{border-radius:var(--re-data-grid-rounded) 0 0 0}.re-dg-header-row:first-child .re-dg-header-cell:last-child{border-radius:0 var(--re-data-grid-rounded) 0 0}.re-dg-data-row:last-child .re-dg-cell:first-child{border-radius:0 0 0 var(--re-data-grid-rounded)}.re-dg-data-row:last-child .re-dg-cell:last-child{border-radius:0 0 var(--re-data-grid-rounded) 0}.re-dg-header-cell .re-dg-header-text{display:-webkit-box;max-height:2.4em;line-height:1.2;white-space:normal;-webkit-box-orient:vertical;-webkit-line-clamp:2;overflow:hidden}.re-dg-row.re-dg-pinned>.re-dg-cell{border-bottom:var(--re-data-grid-pinned-separator);background-color:var(--re-data-grid-pinned-surface)}.re-dg-row .re-dg-header-cell.sticky-left{box-shadow:var(--re-data-grid-sticky-cell-left-shadow);background-color:var(--re-data-grid-sticky-header-cell-surface)}.re-dg-row .re-dg-cell.sticky-left{box-shadow:var(--re-data-grid-sticky-cell-left-shadow);background-color:var(--re-data-grid-sticky-cell-surface)}.re-dg-row .re-dg-header-cell.sticky-right{box-shadow:var(--re-data-grid-sticky-cell-right-shadow);background-color:var(--re-data-grid-sticky-header-cell-surface)}.re-dg-row .re-dg-cell.sticky-right{box-shadow:var(--re-data-grid-sticky-cell-right-shadow);background-color:var(--re-data-grid-sticky-cell-surface)}.sticky-left,.sticky-right{position:sticky;z-index:2}.sortable{cursor:pointer}.active-sort{color:var(--re-data-grid-active)}.re-dg-sort-ind{margin-left:6px}.re-dg-data-row .re-dg-cell.expanded{color:var(--re-data-grid-expanded-color);background:var(--re-data-grid-expanded-surface)}.re-dg-empty{position:absolute;inset:0;display:grid;place-items:center;height:inherit;width:100%;border-radius:var(--re-data-grid-rounded);color:var(--re-data-grid-empty-color);background:var(--re-data-grid-empty-surface)}.re-dg-empty-text{width:100%;text-align:center}.re-dg-loader{position:absolute;inset:0;display:grid;place-items:center;height:inherit;border-radius:var(--re-data-grid-rounded);background-color:var(--re-data-grid-loading-surface);color:var(--re-data-grid-loading-color);z-index:5}.re-dg-tooltip{position:fixed;left:0;top:0;max-width:min(28rem,70vw);padding:var(--re-data-grid-tooltip-padding);border-radius:var(--re-data-grid-tooltip-radius);background:var(--re-data-grid-tooltip-surface);color:var(--re-data-grid-tooltip-color);box-shadow:var(--re-data-grid-tooltip-shadow);font-size:.75rem;line-height:1.2;z-index:var(--re-data-grid-tooltip-z);pointer-events:none;opacity:0;transform:translateY(4px);transition:opacity .12s ease,transform .12s ease}.re-dg-tooltip.visible{opacity:1;transform:translateY(0)}.re-dg-loader-spinner{width:var(--re-data-grid-spinner-size);height:var(--re-data-grid-spinner-size);border-radius:50%;border:var(--re-data-grid-spinner-width) solid var(--re-data-grid-spinner-track-color);border-top-color:var(--re-data-grid-loading-color);animation:re-dg-spinner .8s linear infinite}.re-dg-skeleton-row{display:flex;align-items:center;padding:var(--re-data-grid-cell-paddings);border-bottom:var(--re-data-grid-row-separator);background-color:var(--re-data-grid-cell-surface);pointer-events:none}.re-dg-skeleton-row-line{display:block;width:var(--re-data-grid-skeleton-width);height:var(--re-data-grid-skeleton-height);border-radius:var(--re-data-grid-skeleton-rounded);background:linear-gradient(90deg,var(--re-data-grid-skeleton-line) 0%,var(--re-data-grid-skeleton-shine) 50%,var(--re-data-grid-skeleton-line) 100%);background-size:200% 100%;animation:re-dg-skeleton 1.2s ease-in-out infinite}@keyframes re-dg-skeleton{0%{background-position:200% 0}to{background-position:-200% 0}}@keyframes re-dg-spinner{to{transform:rotate(360deg)}}.re-dg-scrollbar{position:absolute;right:0;top:0;bottom:0;opacity:0;transition:opacity .15s ease-in-out;pointer-events:none;z-index:4}.re-dg-scrollbar.visible{opacity:1}.re-dg-scrollbar-thumb{position:absolute;right:var(--re-data-grid-scrollbar-offset);width:var(--re-data-grid-scrollbar-thumb-size);border-radius:var(--re-data-grid-scrollbar-thumb-rounded);background:var(--re-data-grid-scrollbar-thumb-color);pointer-events:auto;-webkit-user-select:none;user-select:none}.re-dg-spacer{width:1px}.re-dg-top{top:0}.re-dg-bottom{bottom:0}\n"] }]
2517
+ }], ctorParameters: () => [], propDecorators: { data: [{ type: i0.Input, args: [{ isSignal: true, alias: "data", required: false }] }], columns: [{ type: i0.Input, args: [{ isSignal: true, alias: "columns", required: false }] }], mode: [{ type: i0.Input, args: [{ isSignal: true, alias: "mode", required: false }] }], pinnedRows: [{ type: i0.Input, args: [{ isSignal: true, alias: "pinnedRows", required: false }] }], isRowSticky: [{ type: i0.Input, args: [{ isSignal: true, alias: "isRowSticky", required: false }] }], getRowTemplate: [{ type: i0.Input, args: [{ isSignal: true, alias: "getRowTemplate", required: false }] }], hasIndexColumn: [{ type: i0.Input, args: [{ isSignal: true, alias: "hasIndexColumn", required: false }] }], selection: [{ type: i0.Input, args: [{ isSignal: true, alias: "selection", required: false }] }], pageSize: [{ type: i0.Input, args: [{ isSignal: true, alias: "pageSize", required: false }] }], rowHeight: [{ type: i0.Input, args: [{ isSignal: true, alias: "rowHeight", required: false }] }], height: [{ type: i0.Input, args: [{ isSignal: true, alias: "height", required: false }] }], virtualBuffer: [{ type: i0.Input, args: [{ isSignal: true, alias: "virtualBuffer", required: false }] }], headerGroups: [{ type: i0.Input, args: [{ isSignal: true, alias: "headerGroups", required: false }] }], loading: [{ type: i0.Input, args: [{ isSignal: true, alias: "loading", required: false }] }], loadingMode: [{ type: i0.Input, args: [{ isSignal: true, alias: "loadingMode", required: false }] }], rowKey: [{ type: i0.Input, args: [{ isSignal: true, alias: "rowKey", required: false }] }], pageStartFromZero: [{ type: i0.Input, args: [{ isSignal: true, alias: "pageStartFromZero", required: false }] }], pageChange: [{ type: i0.Output, args: ["pageChange"] }], sortChange: [{ type: i0.Output, args: ["sortChange"] }], selectChange: [{ type: i0.Output, args: ["selectChange"] }], rowClick: [{ type: i0.Output, args: ["rowClick"] }], rowContext: [{ type: i0.Output, args: ["rowContext"] }], rowDoubleClick: [{ type: i0.Output, args: ["rowDoubleClick"] }], cellClick: [{ type: i0.Output, args: ["cellClick"] }], cellContext: [{ type: i0.Output, args: ["cellContext"] }], cellDoubleClick: [{ type: i0.Output, args: ["cellDoubleClick"] }], rootEl: [{ type: i0.ViewChild, args: ['root', { isSignal: true }] }], scrollEl: [{ type: i0.ViewChild, args: ['scroll', { isSignal: true }] }], headerEl: [{ type: i0.ViewChild, args: ['header', { isSignal: true }] }], cellTypedSlotRefs: [{ type: i0.ContentChildren, args: [i0.forwardRef(() => DataGridTypeCellTemplateDirective), { isSignal: true }] }], cellDataSlotRefs: [{ type: i0.ContentChildren, args: [i0.forwardRef(() => DataGridCellTemplateDirective), { isSignal: true }] }], declarativeColumnRefs: [{ type: i0.ContentChildren, args: [i0.forwardRef(() => DataGridDeclarativeColumn), { isSignal: true }] }], headerSlotRefs: [{ type: i0.ContentChildren, args: [i0.forwardRef(() => DataGridHeaderTemplateDirective), { isSignal: true }] }], emptySlotRefs: [{ type: i0.ContentChildren, args: [i0.forwardRef(() => DataGridCellEmptyDirective), { isSignal: true }] }], loadingSlotRefs: [{ type: i0.ContentChildren, args: [i0.forwardRef(() => DataGridCellLoadingDirective), { isSignal: true }] }], sortIcSlotRefs: [{ type: i0.ContentChildren, args: [i0.forwardRef(() => DataGridSortIconDirective), { isSignal: true }] }], expanderIcSlotRefs: [{ type: i0.ContentChildren, args: [i0.forwardRef(() => DataGridExpanderIconDirective), { isSignal: true }] }], stickyRowSlotRefs: [{ type: i0.ContentChildren, args: [i0.forwardRef(() => DataGridStickyRowDirective), { isSignal: true }] }], rowSlotRefs: [{ type: i0.ContentChildren, args: [i0.forwardRef(() => DataGridRowDirective), { isSignal: true }] }], tooltipEl: [{ type: i0.ViewChild, args: ['tooltip', { isSignal: true }] }] } });
1828
2518
 
1829
2519
  /**
1830
2520
  * Generated bundle index. Do not edit.
1831
2521
  */
1832
2522
 
1833
- export { DataGrid, DataGridCellEmptyDirective, DataGridCellLoadingDirective, DataGridCellTemplateDirective, DataGridExpanderIconDirective, DataGridHeaderTemplateDirective, DataGridPaginator, DataGridSortIconDirective, DataGridTypeCellTemplateDirective };
2523
+ export { DATA_GRID_CONFIG, DEFAULT_DATA_GRID_DEFAULTS, DataGrid, DataGridCellEmptyDirective, DataGridCellLoadingDirective, DataGridCellTemplateDirective, DataGridDeclarativeCellDirective, DataGridDeclarativeColumn, DataGridDeclarativeHeaderDirective, DataGridExpanderIconDirective, DataGridHeaderTemplateDirective, DataGridRowDirective, DataGridSortIconDirective, DataGridStickyRowDirective, DataGridTypeCellTemplateDirective, provideDataGridDefaults };
1834
2524
  //# sourceMappingURL=reforgium-data-grid.mjs.map