@reforgium/data-grid 2.1.1 → 2.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +61 -32
- package/fesm2022/reforgium-data-grid-grid-overlay-scroll.feature-DRUc6eDp.mjs +79 -0
- package/fesm2022/reforgium-data-grid-grid-selection.feature-CGRNpMeD.mjs +54 -0
- package/fesm2022/reforgium-data-grid-grid-sticky.feature-DBpn_6R8.mjs +86 -0
- package/fesm2022/reforgium-data-grid-grid-tooltip.feature-CMo88m8o.mjs +89 -0
- package/fesm2022/reforgium-data-grid-reforgium-data-grid-Dn9s4YO5.mjs +3274 -0
- package/fesm2022/reforgium-data-grid.mjs +1 -2616
- package/package.json +1 -1
- package/types/reforgium-data-grid.d.ts +89 -18
|
@@ -1,2617 +1,2 @@
|
|
|
1
|
-
|
|
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
|
-
import { NgTemplateOutlet, DatePipe, DecimalPipe } from '@angular/common';
|
|
4
|
-
import { Subscription, Subject, fromEvent, debounceTime } from 'rxjs';
|
|
5
|
-
|
|
6
|
-
/**
|
|
7
|
-
* Directive for defining type-specific cell templates in a data grid.
|
|
8
|
-
*
|
|
9
|
-
* This directive allows developers to create custom cell renderers for specific data types
|
|
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 a column type.
|
|
12
|
-
*
|
|
13
|
-
* Example usage:
|
|
14
|
-
* ```html
|
|
15
|
-
* <ng-template reDataGridTypeCell="date" let-data>
|
|
16
|
-
* {{ data.value | date }}
|
|
17
|
-
* </ng-template>
|
|
18
|
-
* ```
|
|
19
|
-
*
|
|
20
|
-
* The directive captures the template reference and makes it available to the parent
|
|
21
|
-
* data grid component for rendering cells of the specified type.
|
|
22
|
-
*/
|
|
23
|
-
class DataGridTypeCellTemplateDirective {
|
|
24
|
-
/**
|
|
25
|
-
* The type identifier for this cell template.
|
|
26
|
-
*
|
|
27
|
-
* Specifies which data type this template should be used for.
|
|
28
|
-
* When the data grid encounters a column with a matching type,
|
|
29
|
-
* it will use this template to render cells in that column.
|
|
30
|
-
*
|
|
31
|
-
* @default ''
|
|
32
|
-
*/
|
|
33
|
-
type = input('', { ...(ngDevMode ? { debugName: "type" } : {}), alias: 'reDataGridTypeCell' });
|
|
34
|
-
/**
|
|
35
|
-
* Reference to the template defined in the directive.
|
|
36
|
-
*
|
|
37
|
-
* This template will be rendered for each cell of the matching type,
|
|
38
|
-
* receiving `RenderTemplateData` as its context with cell-specific information.
|
|
39
|
-
*/
|
|
40
|
-
tpl = inject((TemplateRef));
|
|
41
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.1", ngImport: i0, type: DataGridTypeCellTemplateDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive });
|
|
42
|
-
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "21.1.1", type: DataGridTypeCellTemplateDirective, isStandalone: true, selector: "ng-template[reDataGridTypeCell]", inputs: { type: { classPropertyName: "type", publicName: "reDataGridTypeCell", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0 });
|
|
43
|
-
}
|
|
44
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.1", ngImport: i0, type: DataGridTypeCellTemplateDirective, decorators: [{
|
|
45
|
-
type: Directive,
|
|
46
|
-
args: [{ selector: 'ng-template[reDataGridTypeCell]' }]
|
|
47
|
-
}], propDecorators: { type: [{ type: i0.Input, args: [{ isSignal: true, alias: "reDataGridTypeCell", required: false }] }] } });
|
|
48
|
-
|
|
49
|
-
class DataGridCellTemplateDirective {
|
|
50
|
-
key = input('', { ...(ngDevMode ? { debugName: "key" } : {}), alias: 'reDataGridCell' });
|
|
51
|
-
/**
|
|
52
|
-
* The injected template reference containing the custom cell template.
|
|
53
|
-
* The template context is of the type `DataGridCellTemplateDirective`.
|
|
54
|
-
*/
|
|
55
|
-
tpl = inject((TemplateRef));
|
|
56
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.1", ngImport: i0, type: DataGridCellTemplateDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive });
|
|
57
|
-
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "21.1.1", type: DataGridCellTemplateDirective, isStandalone: true, selector: "ng-template[reDataGridCell]", inputs: { key: { classPropertyName: "key", publicName: "reDataGridCell", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0 });
|
|
58
|
-
}
|
|
59
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.1", ngImport: i0, type: DataGridCellTemplateDirective, decorators: [{
|
|
60
|
-
type: Directive,
|
|
61
|
-
args: [{ selector: 'ng-template[reDataGridCell]' }]
|
|
62
|
-
}], propDecorators: { key: [{ type: i0.Input, args: [{ isSignal: true, alias: "reDataGridCell", required: false }] }] } });
|
|
63
|
-
|
|
64
|
-
/**
|
|
65
|
-
* Directive for defining custom header templates in data grid columns.
|
|
66
|
-
*
|
|
67
|
-
* This directive allows you to specify a custom template for rendering column headers
|
|
68
|
-
* in the data grid. The template is associated with a column key and receives
|
|
69
|
-
* header-specific context data.
|
|
70
|
-
*
|
|
71
|
-
* @example
|
|
72
|
-
* ```html
|
|
73
|
-
* <ng-template reDataGridHeader="username" let-header>
|
|
74
|
-
* <div class="custom-header">{{ header.label }}</div>
|
|
75
|
-
* </ng-template>
|
|
76
|
-
* ```
|
|
77
|
-
*/
|
|
78
|
-
class DataGridHeaderTemplateDirective {
|
|
79
|
-
/**
|
|
80
|
-
* The column key this header template is associated with.
|
|
81
|
-
* Uses the `reDataGridHeader` directive attribute as an alias.
|
|
82
|
-
* Defaults to an empty string if not provided.
|
|
83
|
-
*/
|
|
84
|
-
key = input('', { ...(ngDevMode ? { debugName: "key" } : {}), alias: 'reDataGridHeader' });
|
|
85
|
-
/**
|
|
86
|
-
* The injected template reference containing the custom header template.
|
|
87
|
-
* The template context is of the type `HeaderTemplateData`.
|
|
88
|
-
*/
|
|
89
|
-
tpl = inject((TemplateRef));
|
|
90
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.1", ngImport: i0, type: DataGridHeaderTemplateDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive });
|
|
91
|
-
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "21.1.1", type: DataGridHeaderTemplateDirective, isStandalone: true, selector: "ng-template[reDataGridHeader]", inputs: { key: { classPropertyName: "key", publicName: "reDataGridHeader", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0 });
|
|
92
|
-
}
|
|
93
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.1", ngImport: i0, type: DataGridHeaderTemplateDirective, decorators: [{
|
|
94
|
-
type: Directive,
|
|
95
|
-
args: [{ selector: 'ng-template[reDataGridHeader]' }]
|
|
96
|
-
}], propDecorators: { key: [{ type: i0.Input, args: [{ isSignal: true, alias: "reDataGridHeader", required: false }] }] } });
|
|
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
|
-
|
|
210
|
-
/**
|
|
211
|
-
* Directive for providing a custom template to display when the data grid has no data.
|
|
212
|
-
*
|
|
213
|
-
* Used as a structural directive on `<ng-template>` elements within data grid components.
|
|
214
|
-
* The template receives a boolean context value indicating the empty state.
|
|
215
|
-
*
|
|
216
|
-
* Example:
|
|
217
|
-
* ```html
|
|
218
|
-
* <ng-template reDataGridEmpty let-isEmpty>
|
|
219
|
-
* <div *ngIf="isEmpty">No data available</div>
|
|
220
|
-
* </ng-template>
|
|
221
|
-
* ```
|
|
222
|
-
*
|
|
223
|
-
* Template context:
|
|
224
|
-
* - `$implicit: boolean` — indicates whether the grid is in an empty state
|
|
225
|
-
*/
|
|
226
|
-
class DataGridCellEmptyDirective {
|
|
227
|
-
tpl = inject((TemplateRef));
|
|
228
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.1", ngImport: i0, type: DataGridCellEmptyDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive });
|
|
229
|
-
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "21.1.1", type: DataGridCellEmptyDirective, isStandalone: true, selector: "ng-template[reDataGridEmpty]", ngImport: i0 });
|
|
230
|
-
}
|
|
231
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.1", ngImport: i0, type: DataGridCellEmptyDirective, decorators: [{
|
|
232
|
-
type: Directive,
|
|
233
|
-
args: [{ selector: 'ng-template[reDataGridEmpty]' }]
|
|
234
|
-
}] });
|
|
235
|
-
/**
|
|
236
|
-
* Directive for providing a custom template to display when the data grid is loading data.
|
|
237
|
-
*
|
|
238
|
-
* Used as a structural directive on `<ng-template>` elements within data grid components.
|
|
239
|
-
* The template receives a boolean context value indicating the loading state.
|
|
240
|
-
*
|
|
241
|
-
* Example:
|
|
242
|
-
* ```html
|
|
243
|
-
* <ng-template reDataGridLoading let-isLoading>
|
|
244
|
-
* <div *ngIf="isLoading">Loading...</div>
|
|
245
|
-
* </ng-template>
|
|
246
|
-
* ```
|
|
247
|
-
*
|
|
248
|
-
* Template context:
|
|
249
|
-
* - `$implicit: boolean` — indicates whether the grid is in loading state
|
|
250
|
-
*/
|
|
251
|
-
class DataGridCellLoadingDirective {
|
|
252
|
-
tpl = inject((TemplateRef));
|
|
253
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.1", ngImport: i0, type: DataGridCellLoadingDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive });
|
|
254
|
-
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "21.1.1", type: DataGridCellLoadingDirective, isStandalone: true, selector: "ng-template[reDataGridLoading]", ngImport: i0 });
|
|
255
|
-
}
|
|
256
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.1", ngImport: i0, type: DataGridCellLoadingDirective, decorators: [{
|
|
257
|
-
type: Directive,
|
|
258
|
-
args: [{ selector: 'ng-template[reDataGridLoading]' }]
|
|
259
|
-
}] });
|
|
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
|
-
|
|
288
|
-
class DataGridSortIconDirective {
|
|
289
|
-
tpl = inject((TemplateRef));
|
|
290
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.1", ngImport: i0, type: DataGridSortIconDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive });
|
|
291
|
-
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "21.1.1", type: DataGridSortIconDirective, isStandalone: true, selector: "ng-template[reDataGridSortIcon]", ngImport: i0 });
|
|
292
|
-
}
|
|
293
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.1", ngImport: i0, type: DataGridSortIconDirective, decorators: [{
|
|
294
|
-
type: Directive,
|
|
295
|
-
args: [{ selector: 'ng-template[reDataGridSortIcon]' }]
|
|
296
|
-
}] });
|
|
297
|
-
class DataGridExpanderIconDirective {
|
|
298
|
-
tpl = inject((TemplateRef));
|
|
299
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.1", ngImport: i0, type: DataGridExpanderIconDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive });
|
|
300
|
-
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "21.1.1", type: DataGridExpanderIconDirective, isStandalone: true, selector: "ng-template[reDataGridExpanderIcon]", ngImport: i0 });
|
|
301
|
-
}
|
|
302
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.1", ngImport: i0, type: DataGridExpanderIconDirective, decorators: [{
|
|
303
|
-
type: Directive,
|
|
304
|
-
args: [{ selector: 'ng-template[reDataGridExpanderIcon]' }]
|
|
305
|
-
}] });
|
|
306
|
-
|
|
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
|
-
deferContent: true,
|
|
319
|
-
deferHeader: false,
|
|
320
|
-
deferPinned: false,
|
|
321
|
-
deferCells: false,
|
|
322
|
-
deferIcons: true,
|
|
323
|
-
deferTooltip: false,
|
|
324
|
-
translations: {
|
|
325
|
-
emptyState: 'No records found',
|
|
326
|
-
itemsPerPageLabel: 'Items per page:',
|
|
327
|
-
nextPageLabel: 'Next page',
|
|
328
|
-
prevPageLabel: 'Previous page',
|
|
329
|
-
},
|
|
330
|
-
debounce: {
|
|
331
|
-
resize: 100,
|
|
332
|
-
scroll: 50,
|
|
333
|
-
},
|
|
334
|
-
};
|
|
335
|
-
const DATA_GRID_CONFIG = new InjectionToken('RE_DATA_GRID_DEFAULTS', {
|
|
336
|
-
providedIn: 'root',
|
|
337
|
-
factory: () => DEFAULT_DATA_GRID_DEFAULTS,
|
|
338
|
-
});
|
|
339
|
-
/**
|
|
340
|
-
* Provides default configuration for Data Grid, overriding base settings.
|
|
341
|
-
*
|
|
342
|
-
* @param config Partial configuration object to be merged with current settings.
|
|
343
|
-
* @returns EnvironmentProviders for use in application or component configuration.
|
|
344
|
-
*/
|
|
345
|
-
function provideDataGridDefaults(config) {
|
|
346
|
-
return makeEnvironmentProviders([
|
|
347
|
-
{
|
|
348
|
-
provide: DATA_GRID_CONFIG,
|
|
349
|
-
useFactory: () => {
|
|
350
|
-
const parent = inject(DATA_GRID_CONFIG, { optional: true, skipSelf: true });
|
|
351
|
-
const base = parent ?? DEFAULT_DATA_GRID_DEFAULTS;
|
|
352
|
-
return {
|
|
353
|
-
...base,
|
|
354
|
-
...config,
|
|
355
|
-
selection: config.selection ?? base.selection,
|
|
356
|
-
translations: { ...base.translations, ...config.translations },
|
|
357
|
-
debounce: { ...base.debounce, ...config.debounce },
|
|
358
|
-
};
|
|
359
|
-
},
|
|
360
|
-
},
|
|
361
|
-
]);
|
|
362
|
-
}
|
|
363
|
-
|
|
364
|
-
const GRID_INDEX_COLUMN = {
|
|
365
|
-
key: '_index',
|
|
366
|
-
type: 'index',
|
|
367
|
-
align: 'center',
|
|
368
|
-
header: '№',
|
|
369
|
-
width: 72,
|
|
370
|
-
sticky: 'left',
|
|
371
|
-
};
|
|
372
|
-
const GRID_CHECKBOX_COLUMN = {
|
|
373
|
-
key: '_checkbox',
|
|
374
|
-
type: 'checkbox',
|
|
375
|
-
align: 'center',
|
|
376
|
-
header: 'checkbox',
|
|
377
|
-
width: 44,
|
|
378
|
-
sticky: 'left',
|
|
379
|
-
};
|
|
380
|
-
|
|
381
|
-
/**
|
|
382
|
-
* Calculates and distributes column widths within a container based on column configuration.
|
|
383
|
-
*
|
|
384
|
-
* The function implements a flexible layout algorithm that:
|
|
385
|
-
* - Respects fixed-width columns (with `width` property set)
|
|
386
|
-
* - Distributes remaining space among auto-sized columns based on their `flex` ratios
|
|
387
|
-
* - Enforces `minWidth` and `maxWidth` constraints
|
|
388
|
-
* - Uses a "water-fill" algorithm to grow columns proportionally
|
|
389
|
-
* - Handles rounding errors to ensure total width matches container width
|
|
390
|
-
*
|
|
391
|
-
* @param columns - Array of column configuration objects with properties
|
|
392
|
-
* like `visible`, `width`, `minWidth`, `maxWidth`, `flex`, and `key`
|
|
393
|
-
* @param containerWidth - Available width in pixels for all columns
|
|
394
|
-
* @param defaultCol - Default minimum width for auto-sized columns without explicit `minWidth`
|
|
395
|
-
* @returns Layout result containing computed widths array, total width, and width lookup by column key
|
|
396
|
-
*
|
|
397
|
-
* @example
|
|
398
|
-
* ```typescript
|
|
399
|
-
* const result = layoutColumns(
|
|
400
|
-
* [
|
|
401
|
-
* { key: 'id', width: 50, visible: true },
|
|
402
|
-
* { key: 'name', minWidth: 100, flex: 2, visible: true },
|
|
403
|
-
* { key: 'age', minWidth: 80, flex: 1, visible: true }
|
|
404
|
-
* ],
|
|
405
|
-
* 800,
|
|
406
|
-
* 100
|
|
407
|
-
* );
|
|
408
|
-
* // result.widths = [50, 500, 250]
|
|
409
|
-
* // result.total = 800
|
|
410
|
-
* // result.byKey = { id: 50, name: 500, age: 250 }
|
|
411
|
-
* ```
|
|
412
|
-
*/
|
|
413
|
-
function layoutColumns(columns, containerWidth, defaultCol) {
|
|
414
|
-
const idx = visibleIndexes(columns);
|
|
415
|
-
const { fixedIdx, autoIdx } = splitFixed(columns, idx);
|
|
416
|
-
const fixedWidths = computeFixedWidths(columns, fixedIdx);
|
|
417
|
-
const baseAuto = computeBaseAuto(columns, autoIdx, defaultCol);
|
|
418
|
-
const sumFixed = sum(fixedWidths);
|
|
419
|
-
const sumBase = sum(baseAuto.map((w) => w.width));
|
|
420
|
-
const free = Math.max(containerWidth - sumFixed - sumBase, 0);
|
|
421
|
-
const grownAuto = distributeFree(columns, baseAuto, free); // water-fill
|
|
422
|
-
const widths = new Array(columns.length).fill(0);
|
|
423
|
-
if (containerWidth <= 0) {
|
|
424
|
-
const byKey = {};
|
|
425
|
-
for (let i = 0; i < columns.length; i++) {
|
|
426
|
-
// noinspection PointlessBooleanExpressionJS
|
|
427
|
-
columns[i].visible !== false && (byKey[columns[i].key] = 0);
|
|
428
|
-
}
|
|
429
|
-
return { widths, total: 0, byKey };
|
|
430
|
-
}
|
|
431
|
-
for (const { index, width } of fixedWidths) {
|
|
432
|
-
widths[index] = width;
|
|
433
|
-
}
|
|
434
|
-
for (const { index, width } of grownAuto) {
|
|
435
|
-
widths[index] = width;
|
|
436
|
-
}
|
|
437
|
-
roundAndFix(columns, widths, fixedIdx, autoIdx, containerWidth);
|
|
438
|
-
let total = 0;
|
|
439
|
-
const byKey = {};
|
|
440
|
-
for (const i of idx) {
|
|
441
|
-
total += widths[i];
|
|
442
|
-
byKey[columns[i].key] = widths[i];
|
|
443
|
-
}
|
|
444
|
-
return { widths, total, byKey };
|
|
445
|
-
}
|
|
446
|
-
/**
|
|
447
|
-
* Filters and returns indexes of visible columns.
|
|
448
|
-
*
|
|
449
|
-
* A column is considered visible if its `visible` property is not explicitly set to `false`.
|
|
450
|
-
*
|
|
451
|
-
* @param cols - Array of column configurations
|
|
452
|
-
* @returns Array of indexes for columns that should be displayed
|
|
453
|
-
*/
|
|
454
|
-
function visibleIndexes(cols) {
|
|
455
|
-
const out = [];
|
|
456
|
-
for (let i = 0; i < cols.length; i++) {
|
|
457
|
-
// noinspection PointlessBooleanExpressionJS
|
|
458
|
-
cols[i].visible !== false && out.push(i);
|
|
459
|
-
}
|
|
460
|
-
return out;
|
|
461
|
-
}
|
|
462
|
-
/**
|
|
463
|
-
* Splits visible column indexes into fixed-width and auto-sized groups.
|
|
464
|
-
*
|
|
465
|
-
* Fixed columns have an explicit `width` property defined.
|
|
466
|
-
* Auto-sized columns have `width` as `undefined` and will grow to fill available space.
|
|
467
|
-
*
|
|
468
|
-
* @param cols - Array of column configurations
|
|
469
|
-
* @param visibleIdx - Array of visible column indexes
|
|
470
|
-
* @returns Object with `fixedIdx` (fixed-width column indexes) and `autoIdx` (auto-sized column indexes)
|
|
471
|
-
*/
|
|
472
|
-
function splitFixed(cols, visibleIdx) {
|
|
473
|
-
const fixedIdx = [];
|
|
474
|
-
const autoIdx = [];
|
|
475
|
-
for (const i of visibleIdx) {
|
|
476
|
-
cols[i].width === undefined ? autoIdx.push(i) : fixedIdx.push(i);
|
|
477
|
-
}
|
|
478
|
-
return { fixedIdx, autoIdx };
|
|
479
|
-
}
|
|
480
|
-
/**
|
|
481
|
-
* Computes actual widths for fixed-width columns, respecting min/max constraints.
|
|
482
|
-
*
|
|
483
|
-
* Takes the explicit `width` value and clamps it between `minWidth` and `maxWidth`.
|
|
484
|
-
*
|
|
485
|
-
* @param cols - Array of column configurations
|
|
486
|
-
* @param fixedIdx - Array of fixed-width column indexes
|
|
487
|
-
* @returns Array of objects with column index and computed width
|
|
488
|
-
*/
|
|
489
|
-
const computeFixedWidths = (cols, fixedIdx) => fixedIdx.map((index) => {
|
|
490
|
-
const col = cols[index];
|
|
491
|
-
const width = clamp$1(col.width, col.minWidth, col.maxWidth);
|
|
492
|
-
return { index, width };
|
|
493
|
-
});
|
|
494
|
-
/**
|
|
495
|
-
* Computes base (minimum) widths for auto-sized columns before distribution of free space.
|
|
496
|
-
*
|
|
497
|
-
* Uses column's `minWidth` if specified, otherwise falls back to the default value.
|
|
498
|
-
* The base width is clamped between `minWidth` and `maxWidth` constraints.
|
|
499
|
-
*
|
|
500
|
-
* @param cols - Array of column configurations
|
|
501
|
-
* @param autoIdx - Array of auto-sized column indexes
|
|
502
|
-
* @param def - Default minimum width to use when column's `minWidth` is not specified
|
|
503
|
-
* @returns Array of objects with column index and base width
|
|
504
|
-
*/
|
|
505
|
-
const computeBaseAuto = (cols, autoIdx, def) => autoIdx.map((index) => {
|
|
506
|
-
const c = cols[index];
|
|
507
|
-
const base = clamp$1(c.minWidth ?? def, c.minWidth, c.maxWidth);
|
|
508
|
-
return { index, width: base };
|
|
509
|
-
});
|
|
510
|
-
/**
|
|
511
|
-
* Distributes free space among auto-sized columns using a "water-fill" algorithm.
|
|
512
|
-
*
|
|
513
|
-
* The algorithm works iteratively:
|
|
514
|
-
* 1. Identifies columns that can still grow (haven't reached `maxWidth`)
|
|
515
|
-
* 2. Distributes remaining space proportionally based on each column's `flex` ratio
|
|
516
|
-
* 3. Respects `maxWidth` constraints by capping growth
|
|
517
|
-
* 4. Repeats until no space remains or no columns can grow further
|
|
518
|
-
*
|
|
519
|
-
* This ensures that columns with higher `flex` values receive proportionally more space,
|
|
520
|
-
* while respecting all width constraints.
|
|
521
|
-
*
|
|
522
|
-
* @param cols - Array of column configurations
|
|
523
|
-
* @param base - Array of column indexes with their base widths before distribution
|
|
524
|
-
* @param free - Amount of free space (in pixels) to distribute among columns
|
|
525
|
-
* @returns Array of objects with column index and final width after distribution
|
|
526
|
-
*/
|
|
527
|
-
function distributeFree(cols, base, free) {
|
|
528
|
-
if (free <= 0 || base.length === 0) {
|
|
529
|
-
return base;
|
|
530
|
-
}
|
|
531
|
-
const widths = base.map((b) => ({ ...b }));
|
|
532
|
-
let remaining = free;
|
|
533
|
-
let growable = widths.filter((w) => widthsCanGrow(cols, w.index, w.width)).map((w) => w.index);
|
|
534
|
-
while (remaining > 0 && growable.length > 0) {
|
|
535
|
-
const flexSum = growable.reduce((s, i) => s + (cols[i].flex ?? 1), 0) || 1;
|
|
536
|
-
let distributed = 0;
|
|
537
|
-
for (const i of growable) {
|
|
538
|
-
const rec = widths.find((w) => w.index === i);
|
|
539
|
-
const cap = (cols[i].maxWidth ?? Infinity) - rec.width;
|
|
540
|
-
if (cap <= 0) {
|
|
541
|
-
continue;
|
|
542
|
-
}
|
|
543
|
-
const ideal = remaining * ((cols[i].flex ?? 1) / flexSum);
|
|
544
|
-
const add = ideal < cap ? ideal : cap;
|
|
545
|
-
if (add > 0) {
|
|
546
|
-
rec.width += add;
|
|
547
|
-
distributed += add;
|
|
548
|
-
}
|
|
549
|
-
}
|
|
550
|
-
if (distributed <= 1e-6) {
|
|
551
|
-
break;
|
|
552
|
-
}
|
|
553
|
-
remaining -= distributed;
|
|
554
|
-
growable = widths.filter((w) => widthsCanGrow(cols, w.index, w.width)).map((w) => w.index);
|
|
555
|
-
}
|
|
556
|
-
return widths;
|
|
557
|
-
}
|
|
558
|
-
/**
|
|
559
|
-
* Checks whether a column can grow beyond its current width.
|
|
560
|
-
*
|
|
561
|
-
* A column can grow if its current width is less than its `maxWidth` constraint
|
|
562
|
-
* (or `Infinity` if no max is specified), with a small epsilon tolerance.
|
|
563
|
-
*
|
|
564
|
-
* @param cols - Array of column configurations
|
|
565
|
-
* @param i - Column index to check
|
|
566
|
-
* @param cur - Current width of the column
|
|
567
|
-
* @returns `true` if the column can grow, `false` otherwise
|
|
568
|
-
*/
|
|
569
|
-
function widthsCanGrow(cols, i, cur) {
|
|
570
|
-
const max = cols[i].maxWidth ?? Infinity;
|
|
571
|
-
return cur < max - 1e-6;
|
|
572
|
-
}
|
|
573
|
-
/**
|
|
574
|
-
* Rounds auto-sized column widths to integers and distributes any remaining pixels to match container width exactly.
|
|
575
|
-
*
|
|
576
|
-
* This function handles rounding errors that occur when distributing fractional widths:
|
|
577
|
-
* 1. Floors all auto-sized column widths to integers
|
|
578
|
-
* 2. Calculates how many pixels are needed to reach the exact container width
|
|
579
|
-
* 3. Distributes remaining pixels one-by-one to columns (right-to-left) that haven't reached their `maxWidth`
|
|
580
|
-
*
|
|
581
|
-
* Fixed-width columns are not modified. This ensures the total width matches the container precisely.
|
|
582
|
-
*
|
|
583
|
-
* @param cols - Array of column configurations
|
|
584
|
-
* @param widths - Array of column widths to be modified in place
|
|
585
|
-
* @param fixedIdx - Array of fixed-width column indexes (not modified)
|
|
586
|
-
* @param autoIdx - Array of auto-sized column indexes (will be rounded and adjusted)
|
|
587
|
-
* @param containerWidth - Target total width that must be matched exactly
|
|
588
|
-
*/
|
|
589
|
-
function roundAndFix(cols, widths, fixedIdx, autoIdx, containerWidth) {
|
|
590
|
-
const sumFixed = fixedIdx.reduce((s, i) => s + widths[i], 0);
|
|
591
|
-
let sumAuto = 0;
|
|
592
|
-
for (const i of autoIdx) {
|
|
593
|
-
widths[i] = Math.floor(widths[i]);
|
|
594
|
-
sumAuto += widths[i];
|
|
595
|
-
}
|
|
596
|
-
let need = Math.round(Math.max(containerWidth - sumFixed - sumAuto, 0));
|
|
597
|
-
if (need === 0) {
|
|
598
|
-
return;
|
|
599
|
-
}
|
|
600
|
-
for (let k = autoIdx.length - 1; k >= 0 && need > 0; k--) {
|
|
601
|
-
const i = autoIdx[k];
|
|
602
|
-
const max = cols[i].maxWidth ?? Infinity;
|
|
603
|
-
if (widths[i] + 1 <= max) {
|
|
604
|
-
widths[i] += 1;
|
|
605
|
-
need--;
|
|
606
|
-
}
|
|
607
|
-
}
|
|
608
|
-
}
|
|
609
|
-
/**
|
|
610
|
-
* Clamps a numeric value between optional minimum and maximum bounds.
|
|
611
|
-
*
|
|
612
|
-
* If `min` is provided and the value is less than it, returns `min`.
|
|
613
|
-
* If `max` is provided and the value is greater than it, returns `max`.
|
|
614
|
-
* Otherwise, returns the original value.
|
|
615
|
-
*
|
|
616
|
-
* @param v - Value to clamp
|
|
617
|
-
* @param min - Optional minimum bound
|
|
618
|
-
* @param max - Optional maximum bound
|
|
619
|
-
* @returns Clamped value
|
|
620
|
-
*/
|
|
621
|
-
function clamp$1(v, min, max) {
|
|
622
|
-
min != null && v < min && (v = min);
|
|
623
|
-
max != null && v > max && (v = max);
|
|
624
|
-
return v;
|
|
625
|
-
}
|
|
626
|
-
/**
|
|
627
|
-
* Calculates the sum of numeric values or width properties in an array.
|
|
628
|
-
*
|
|
629
|
-
* Supports two input formats:
|
|
630
|
-
* - Array of numbers: sums all numeric values
|
|
631
|
-
* - Array of objects with `width` property: sums all `width` values
|
|
632
|
-
*
|
|
633
|
-
* @param items - Array of numbers or objects with `width` property
|
|
634
|
-
* @returns Sum of all values
|
|
635
|
-
*/
|
|
636
|
-
function sum(items) {
|
|
637
|
-
if (Array.isArray(items) && typeof items[0] === 'number') {
|
|
638
|
-
return items.reduce((s, x) => s + x, 0);
|
|
639
|
-
}
|
|
640
|
-
return items.reduce((s, x) => s + x.width, 0);
|
|
641
|
-
}
|
|
642
|
-
|
|
643
|
-
// noinspection ES6PreferShortImport
|
|
644
|
-
const DEFAULT_COLUMN_WIDTH = 140;
|
|
645
|
-
/**
|
|
646
|
-
* Computes and normalizes header groups based on the provided headers and columns.
|
|
647
|
-
*
|
|
648
|
-
* @param {GridHeaderGroup<Data>[]} headers - An array of header group objects representing the grouping information
|
|
649
|
-
* for the grid.
|
|
650
|
-
* @param {GridColumn<Data>[]} columns - An array of column definitions describing the columns in the grid.
|
|
651
|
-
* @return {NormalizedHeader[]} An array of normalized header objects, where each object contains information about
|
|
652
|
-
* the computed header ranges, widths, and keys.
|
|
653
|
-
*/
|
|
654
|
-
function computeHeaderGroups(headers = [], columns = []) {
|
|
655
|
-
if (!columns.length || !headers.length) {
|
|
656
|
-
return [];
|
|
657
|
-
}
|
|
658
|
-
const keyToIndex = new Map(columns.map((col, index) => [col.key, index]));
|
|
659
|
-
const columnWidths = columns.map(resolveColumnWidth);
|
|
660
|
-
const prefix = buildWidthesPrefixSums(columnWidths);
|
|
661
|
-
const ranges = [];
|
|
662
|
-
const occupied = new Set();
|
|
663
|
-
for (const header of headers) {
|
|
664
|
-
const start = keyToIndex.get(header.from);
|
|
665
|
-
const resolvedTo = header.to ?? header.from;
|
|
666
|
-
const end = keyToIndex.get(resolvedTo);
|
|
667
|
-
if (start === undefined || end === undefined) {
|
|
668
|
-
continue;
|
|
669
|
-
}
|
|
670
|
-
const rangeStart = Math.min(start, end);
|
|
671
|
-
const rangeEnd = Math.max(start, end);
|
|
672
|
-
if (hasOverlap(rangeStart, rangeEnd, occupied)) {
|
|
673
|
-
continue;
|
|
674
|
-
}
|
|
675
|
-
for (let i = rangeStart; i <= rangeEnd; i++) {
|
|
676
|
-
occupied.add(i);
|
|
677
|
-
}
|
|
678
|
-
ranges.push({ header, start: rangeStart, end: rangeEnd });
|
|
679
|
-
}
|
|
680
|
-
const rangesByStart = new Map(ranges.map((range) => [range.start, range]));
|
|
681
|
-
const normalized = [];
|
|
682
|
-
for (let index = 0; index < columns.length;) {
|
|
683
|
-
const range = rangesByStart.get(index);
|
|
684
|
-
if (range) {
|
|
685
|
-
normalized.push(normalizeRange(range, prefix, columns));
|
|
686
|
-
index = range.end + 1;
|
|
687
|
-
continue;
|
|
688
|
-
}
|
|
689
|
-
const start = index;
|
|
690
|
-
while (index < columns.length && !rangesByStart.has(index)) {
|
|
691
|
-
index++;
|
|
692
|
-
}
|
|
693
|
-
const end = index - 1;
|
|
694
|
-
normalized.push({
|
|
695
|
-
key: `_rest_${columns[start].key}_${columns[end].key}`,
|
|
696
|
-
widthPx: sumByRange(prefix, start, end),
|
|
697
|
-
startKey: columns[start]?.key,
|
|
698
|
-
endKey: columns[end]?.key,
|
|
699
|
-
});
|
|
700
|
-
}
|
|
701
|
-
return normalized;
|
|
702
|
-
}
|
|
703
|
-
function normalizeRange(range, prefix, columns) {
|
|
704
|
-
const base = {
|
|
705
|
-
key: range.header.key,
|
|
706
|
-
widthPx: sumByRange(prefix, range.start, range.end),
|
|
707
|
-
startKey: columns[range.start]?.key,
|
|
708
|
-
endKey: columns[range.end]?.key,
|
|
709
|
-
};
|
|
710
|
-
if ('titleTemplate' in range.header) {
|
|
711
|
-
base.titleTemplate = range.header.titleTemplate;
|
|
712
|
-
return base;
|
|
713
|
-
}
|
|
714
|
-
base.title = range.header.title;
|
|
715
|
-
base.align = range.header.align;
|
|
716
|
-
return base;
|
|
717
|
-
}
|
|
718
|
-
function resolveColumnWidth(column) {
|
|
719
|
-
const width = asFiniteNumber(column.width);
|
|
720
|
-
const minWidth = asFiniteNumber(column.minWidth);
|
|
721
|
-
const maxWidth = asFiniteNumber(column.maxWidth);
|
|
722
|
-
let result = width ?? minWidth ?? DEFAULT_COLUMN_WIDTH;
|
|
723
|
-
if (minWidth !== null && result < minWidth) {
|
|
724
|
-
result = minWidth;
|
|
725
|
-
}
|
|
726
|
-
if (maxWidth !== null && result > maxWidth) {
|
|
727
|
-
result = maxWidth;
|
|
728
|
-
}
|
|
729
|
-
return Math.max(0, Math.round(result));
|
|
730
|
-
}
|
|
731
|
-
function asFiniteNumber(value) {
|
|
732
|
-
return typeof value === 'number' && Number.isFinite(value) ? value : null;
|
|
733
|
-
}
|
|
734
|
-
function hasOverlap(start, end, occupied) {
|
|
735
|
-
for (let i = start; i <= end; i++) {
|
|
736
|
-
if (occupied.has(i)) {
|
|
737
|
-
return true;
|
|
738
|
-
}
|
|
739
|
-
}
|
|
740
|
-
return false;
|
|
741
|
-
}
|
|
742
|
-
function buildWidthesPrefixSums(values) {
|
|
743
|
-
const prefix = new Array(values.length + 1).fill(0);
|
|
744
|
-
for (let i = 0; i < values.length; i++) {
|
|
745
|
-
prefix[i + 1] = prefix[i] + values[i];
|
|
746
|
-
}
|
|
747
|
-
return prefix;
|
|
748
|
-
}
|
|
749
|
-
function sumByRange(prefix, start, end) {
|
|
750
|
-
return prefix[end + 1] - prefix[start];
|
|
751
|
-
}
|
|
752
|
-
|
|
753
|
-
/**
|
|
754
|
-
* Calculates the visual state of an overlay scrollbar. All arguments are raw DOM numbers.
|
|
755
|
-
* Returns a state describing the visibility, size, and position of the thumb.
|
|
756
|
-
*/
|
|
757
|
-
function computeScrollbarState(scrollHeight = 0, clientHeight = 0, scrollTop = 0, minThumb = 24) {
|
|
758
|
-
const safeClient = toSafe(clientHeight);
|
|
759
|
-
const safeScrollH = toSafe(scrollHeight);
|
|
760
|
-
const safeTop = clamp(scrollTop, 0, Math.max(0, safeScrollH - safeClient));
|
|
761
|
-
if (safeClient <= 0 || !isFinite(safeClient)) {
|
|
762
|
-
return hiddenState();
|
|
763
|
-
}
|
|
764
|
-
if (safeScrollH <= safeClient + 1) {
|
|
765
|
-
return hiddenState(safeClient);
|
|
766
|
-
}
|
|
767
|
-
const track = safeClient;
|
|
768
|
-
const ratio = track / safeScrollH;
|
|
769
|
-
const thumb = Math.max(minThumb, Math.floor(track * ratio));
|
|
770
|
-
const maxScrollTop = Math.max(1, safeScrollH - track);
|
|
771
|
-
const maxThumbTop = Math.max(1, track - thumb);
|
|
772
|
-
const topRaw = Math.round((safeTop / maxScrollTop) * maxThumbTop);
|
|
773
|
-
const thumbTop = clamp(isFinite(topRaw) ? topRaw : 0, 0, maxThumbTop);
|
|
774
|
-
return {
|
|
775
|
-
visible: true,
|
|
776
|
-
thumbHeight: thumb,
|
|
777
|
-
thumbTop,
|
|
778
|
-
track,
|
|
779
|
-
maxThumbTop,
|
|
780
|
-
maxScrollTop,
|
|
781
|
-
};
|
|
782
|
-
}
|
|
783
|
-
/** Converts thumb position thumbTop (0..maxThumbTop) to scroll position scrollTop (0..maxScrollTop) */
|
|
784
|
-
function mapThumbTopToScrollTop(thumbTop, maxThumbTop, maxScrollTop) {
|
|
785
|
-
const mt = Math.max(1, toSafe(maxThumbTop));
|
|
786
|
-
const ms = Math.max(1, toSafe(maxScrollTop));
|
|
787
|
-
const tt = clamp(toSafe(thumbTop), 0, mt);
|
|
788
|
-
return (tt / mt) * ms;
|
|
789
|
-
}
|
|
790
|
-
/** Clamps the new thumb position within bounds 0..maxThumbTop */
|
|
791
|
-
function clampThumbTop(value, maxThumbTop) {
|
|
792
|
-
const mt = Math.max(1, toSafe(maxThumbTop));
|
|
793
|
-
return clamp(toSafe(value), 0, mt);
|
|
794
|
-
}
|
|
795
|
-
function hiddenState(track = 0) {
|
|
796
|
-
return {
|
|
797
|
-
visible: false,
|
|
798
|
-
thumbHeight: 0,
|
|
799
|
-
thumbTop: 0,
|
|
800
|
-
track,
|
|
801
|
-
maxThumbTop: 1,
|
|
802
|
-
maxScrollTop: 1,
|
|
803
|
-
};
|
|
804
|
-
}
|
|
805
|
-
function toSafe(n) {
|
|
806
|
-
return Number.isFinite(n) ? n : 0;
|
|
807
|
-
}
|
|
808
|
-
function clamp(v, min, max) {
|
|
809
|
-
if (v < min) {
|
|
810
|
-
return min;
|
|
811
|
-
}
|
|
812
|
-
if (v > max) {
|
|
813
|
-
return max;
|
|
814
|
-
}
|
|
815
|
-
return v;
|
|
816
|
-
}
|
|
817
|
-
|
|
818
|
-
/**
|
|
819
|
-
* Detects the primary scroll direction of an HTML element.
|
|
820
|
-
*
|
|
821
|
-
* Compares the current scroll position with the previous one to determine
|
|
822
|
-
* whether the user is scrolling horizontally, vertically, or not at all.
|
|
823
|
-
*
|
|
824
|
-
* Example:
|
|
825
|
-
* ```typescript
|
|
826
|
-
* const detector = new ScrollDirectionDetector(scrollableDiv);
|
|
827
|
-
*
|
|
828
|
-
* scrollableDiv.addEventListener('scroll', () => {
|
|
829
|
-
* const direction = detector.detect();
|
|
830
|
-
* console.log('Scroll direction:', direction); // 'horizontal' | 'vertical' | 'none'
|
|
831
|
-
* });
|
|
832
|
-
* ```
|
|
833
|
-
*
|
|
834
|
-
* The detector stores the previous scroll position internally and updates it
|
|
835
|
-
* on each `detect()` call to track movement deltas.
|
|
836
|
-
*/
|
|
837
|
-
class ScrollDirectionDetector {
|
|
838
|
-
el;
|
|
839
|
-
/**
|
|
840
|
-
* Previously recorded vertical scroll position (scrollTop).
|
|
841
|
-
* @private
|
|
842
|
-
*/
|
|
843
|
-
#prevTop = 0;
|
|
844
|
-
/**
|
|
845
|
-
* Previously recorded horizontal scroll position (scrollLeft).
|
|
846
|
-
* @private
|
|
847
|
-
*/
|
|
848
|
-
#prevLeft = 0;
|
|
849
|
-
/**
|
|
850
|
-
* Creates a new scroll direction detector for the given HTML element.
|
|
851
|
-
*
|
|
852
|
-
* @param el - The HTML element whose scroll direction will be tracked.
|
|
853
|
-
* The element must be scrollable (have overflow content).
|
|
854
|
-
*/
|
|
855
|
-
constructor(el) {
|
|
856
|
-
this.el = el;
|
|
857
|
-
this.#prevTop = el.scrollTop;
|
|
858
|
-
this.#prevLeft = el.scrollLeft;
|
|
859
|
-
}
|
|
860
|
-
/**
|
|
861
|
-
* Detects the primary scroll direction based on position changes since the last call.
|
|
862
|
-
*
|
|
863
|
-
* Compares the absolute deltas of horizontal and vertical scroll positions.
|
|
864
|
-
* The direction with the greater delta is considered the primary scroll direction.
|
|
865
|
-
*
|
|
866
|
-
* @returns The detected scroll direction:
|
|
867
|
-
* - `'horizontal'` if horizontal movement is greater
|
|
868
|
-
* - `'vertical'` if vertical movement is greater
|
|
869
|
-
* - `'none'` if no scroll movement occurred or deltas are equal
|
|
870
|
-
*/
|
|
871
|
-
detect() {
|
|
872
|
-
const { scrollTop, scrollLeft } = this.el;
|
|
873
|
-
const deltaX = Math.abs(scrollLeft - this.#prevLeft);
|
|
874
|
-
const deltaY = Math.abs(scrollTop - this.#prevTop);
|
|
875
|
-
this.#prevTop = scrollTop;
|
|
876
|
-
this.#prevLeft = scrollLeft;
|
|
877
|
-
if (deltaX === 0 && deltaY === 0)
|
|
878
|
-
return 'none';
|
|
879
|
-
if (deltaX > deltaY)
|
|
880
|
-
return 'horizontal';
|
|
881
|
-
if (deltaY > deltaX)
|
|
882
|
-
return 'vertical';
|
|
883
|
-
return 'none';
|
|
884
|
-
}
|
|
885
|
-
}
|
|
886
|
-
|
|
887
|
-
/**
|
|
888
|
-
* Splits an array of grid columns into sticky left, sticky right, and all visible columns.
|
|
889
|
-
*
|
|
890
|
-
* This function filters columns by visibility, then divides them at the midpoint
|
|
891
|
-
* to identify which sticky columns should be pinned to the left versus right side
|
|
892
|
-
* of the data grid.
|
|
893
|
-
*
|
|
894
|
-
* @template Data - The data type extending `AnyDict` that the grid columns are based on.
|
|
895
|
-
* @param cols - Array of grid columns to be split.
|
|
896
|
-
* @returns An object containing:
|
|
897
|
-
* - `left`: Array of sticky columns from the first half of visible columns.
|
|
898
|
-
* - `right`: Array of sticky columns from the second half of visible columns.
|
|
899
|
-
* - `visible`: Array of all visible columns (filtered by `visible !== false`).
|
|
900
|
-
*
|
|
901
|
-
* @example
|
|
902
|
-
* ```typescript
|
|
903
|
-
* const columns: GridColumn<User>[] = [...];
|
|
904
|
-
* const { left, right, visible } = splitSticky(columns);
|
|
905
|
-
* ```
|
|
906
|
-
*/
|
|
907
|
-
function splitSticky(cols) {
|
|
908
|
-
const visible = cols.filter((c) => c.visible !== false);
|
|
909
|
-
const left = visible.filter((col) => col.sticky === 'left' || col.sticky === true);
|
|
910
|
-
const right = visible.filter((col) => col.sticky === 'right');
|
|
911
|
-
return { left, right, visible };
|
|
912
|
-
}
|
|
913
|
-
|
|
914
|
-
/**
|
|
915
|
-
* View model for the data grid component.
|
|
916
|
-
*
|
|
917
|
-
* Manages grid state including column layout, sticky positioning, scrollbar calculations,
|
|
918
|
-
* and pinned rows. Automatically recomputes column widths based on container size and
|
|
919
|
-
* handles sticky column offsets for left and right pinned columns.
|
|
920
|
-
*
|
|
921
|
-
* @template Data - Type of data objects in the grid, must extend AnyDict
|
|
922
|
-
*
|
|
923
|
-
* @example
|
|
924
|
-
* ```typescript
|
|
925
|
-
* const gridVm = inject(DataGridVm<MyDataType>);
|
|
926
|
-
* gridVm.columns.set([{ key: 'name', title: 'Name' }]);
|
|
927
|
-
* gridVm.containerWidth.set(800);
|
|
928
|
-
* ```
|
|
929
|
-
*/
|
|
930
|
-
class DataGridVm {
|
|
931
|
-
/**
|
|
932
|
-
* Reference to the scrollable container element.
|
|
933
|
-
*
|
|
934
|
-
* Used for scrollbar calculations and scroll position management.
|
|
935
|
-
*/
|
|
936
|
-
scrollEl = signal(undefined, ...(ngDevMode ? [{ debugName: "scrollEl" }] : []));
|
|
937
|
-
/**
|
|
938
|
-
* Array of column configurations for the grid.
|
|
939
|
-
*
|
|
940
|
-
* Defines all columns including their keys, titles, sticky positioning, and widths.
|
|
941
|
-
* Value is reactive and triggers column layout recalculation when changed.
|
|
942
|
-
*/
|
|
943
|
-
columns = signal([], ...(ngDevMode ? [{ debugName: "columns" }] : []));
|
|
944
|
-
/**
|
|
945
|
-
* Array of pinned row configurations.
|
|
946
|
-
*
|
|
947
|
-
* Defines rows that remain fixed at the top or bottom of the grid during scrolling.
|
|
948
|
-
* Each row includes data, position ('top' or 'bottom'), and optional ordering.
|
|
949
|
-
*/
|
|
950
|
-
pinnedRows = signal([], ...(ngDevMode ? [{ debugName: "pinnedRows" }] : []));
|
|
951
|
-
headerGroups = signal([], ...(ngDevMode ? [{ debugName: "headerGroups" }] : []));
|
|
952
|
-
/**
|
|
953
|
-
* Current width of the grid container in pixels.
|
|
954
|
-
*
|
|
955
|
-
* Used for column width calculations and layout adjustments.
|
|
956
|
-
* Value is reactive and triggers column layout recalculation when changed.
|
|
957
|
-
*/
|
|
958
|
-
containerWidth = signal(0, ...(ngDevMode ? [{ debugName: "containerWidth" }] : []));
|
|
959
|
-
/**
|
|
960
|
-
* Flag indicating whether the custom scrollbar should be visible.
|
|
961
|
-
*
|
|
962
|
-
* Automatically computed based on content height vs. container height.
|
|
963
|
-
*/
|
|
964
|
-
scrollbarVisible = signal(false, ...(ngDevMode ? [{ debugName: "scrollbarVisible" }] : []));
|
|
965
|
-
/**
|
|
966
|
-
* Height of the scrollbar thumb in pixels.
|
|
967
|
-
*
|
|
968
|
-
* Proportional to the ratio of visible content to total content height.
|
|
969
|
-
*/
|
|
970
|
-
thumbHeightPx = signal(0, ...(ngDevMode ? [{ debugName: "thumbHeightPx" }] : []));
|
|
971
|
-
/**
|
|
972
|
-
* Top position of the scrollbar thumb in pixels.
|
|
973
|
-
*
|
|
974
|
-
* Corresponds to the current scroll position within the scrollable area.
|
|
975
|
-
*/
|
|
976
|
-
thumbTopPx = signal(0, ...(ngDevMode ? [{ debugName: "thumbTopPx" }] : []));
|
|
977
|
-
/**
|
|
978
|
-
* Flag indicating whether the user is currently dragging the scrollbar thumb.
|
|
979
|
-
*
|
|
980
|
-
* Used to track active scroll drag interactions.
|
|
981
|
-
*/
|
|
982
|
-
dragging = false;
|
|
983
|
-
/**
|
|
984
|
-
* Map of global cell renderer templates by type.
|
|
985
|
-
*
|
|
986
|
-
* Stores reusable template references for different cell renderer types
|
|
987
|
-
* that can be shared across multiple columns.
|
|
988
|
-
*/
|
|
989
|
-
globalTypeCellTpls = new Map();
|
|
990
|
-
globalDataCellTpls = new Map();
|
|
991
|
-
globalRowCellTpls = new Map();
|
|
992
|
-
#byKey = signal({}, ...(ngDevMode ? [{ debugName: "#byKey" }] : []));
|
|
993
|
-
#defaultColWidth = 140;
|
|
994
|
-
#stickyLeftMap = new Map();
|
|
995
|
-
#stickyRightMap = new Map();
|
|
996
|
-
#stickySplit = computed(() => splitSticky(this.columns() ?? []), ...(ngDevMode ? [{ debugName: "#stickySplit" }] : []));
|
|
997
|
-
#layoutSignature = '';
|
|
998
|
-
#stickySignature = '';
|
|
999
|
-
pinnedTop = signal([], ...(ngDevMode ? [{ debugName: "pinnedTop" }] : []));
|
|
1000
|
-
pinnedBottom = signal([], ...(ngDevMode ? [{ debugName: "pinnedBottom" }] : []));
|
|
1001
|
-
/**
|
|
1002
|
-
* Computed an array of non-sticky columns to display in the scrollable area.
|
|
1003
|
-
*
|
|
1004
|
-
* Automatically splits columns into left-sticky, visible, and right-sticky groups,
|
|
1005
|
-
* recomputes sticky offsets, and returns only the scrollable middle section.
|
|
1006
|
-
* Recalculates whenever columns or container width changes.
|
|
1007
|
-
*/
|
|
1008
|
-
columnsToShow = computed(() => {
|
|
1009
|
-
this.containerWidth();
|
|
1010
|
-
return this.#stickySplit().visible;
|
|
1011
|
-
}, ...(ngDevMode ? [{ debugName: "columnsToShow" }] : []));
|
|
1012
|
-
contentWidth = computed(() => {
|
|
1013
|
-
const columns = this.columnsToShow();
|
|
1014
|
-
if (!columns.length) {
|
|
1015
|
-
return 0;
|
|
1016
|
-
}
|
|
1017
|
-
return columns.reduce((sum, col) => sum + this.widthByKey(col.key), 0);
|
|
1018
|
-
}, ...(ngDevMode ? [{ debugName: "contentWidth" }] : []));
|
|
1019
|
-
/**
|
|
1020
|
-
* Computed array of normalized header groups with calculated widths.
|
|
1021
|
-
*
|
|
1022
|
-
* Transforms raw header group configurations into normalized structures
|
|
1023
|
-
* that include computed column widths for layout rendering. Returns an empty
|
|
1024
|
-
* array if no headers or columns are configured. Recalculates whenever
|
|
1025
|
-
* header groups or visible columns change.
|
|
1026
|
-
*
|
|
1027
|
-
* @returns Array of normalized header configurations with width calculations
|
|
1028
|
-
*/
|
|
1029
|
-
normalizedHeaderGroups = computed(() => {
|
|
1030
|
-
const headers = this.headerGroups();
|
|
1031
|
-
const columns = this.columnsToShow();
|
|
1032
|
-
if (!headers.length || !columns.length) {
|
|
1033
|
-
return [];
|
|
1034
|
-
}
|
|
1035
|
-
const normalizedColumns = columns.map((col) => ({
|
|
1036
|
-
...col,
|
|
1037
|
-
width: this.widthByKey(col.key),
|
|
1038
|
-
}));
|
|
1039
|
-
return computeHeaderGroups(headers, normalizedColumns);
|
|
1040
|
-
}, ...(ngDevMode ? [{ debugName: "normalizedHeaderGroups" }] : []));
|
|
1041
|
-
constructor() {
|
|
1042
|
-
effect(() => {
|
|
1043
|
-
const rows = this.pinnedRows() ?? [];
|
|
1044
|
-
this.pinnedTop.set(rows.filter((r) => r.position === 'top').sort((a, b) => (a.order ?? 0) - (b.order ?? 0)));
|
|
1045
|
-
this.pinnedBottom.set(rows.filter((r) => r.position === 'bottom').sort((a, b) => (a.order ?? 0) - (b.order ?? 0)));
|
|
1046
|
-
});
|
|
1047
|
-
effect(() => {
|
|
1048
|
-
const cols = this.columns();
|
|
1049
|
-
const width = this.containerWidth();
|
|
1050
|
-
if (!cols.length || !width) {
|
|
1051
|
-
return;
|
|
1052
|
-
}
|
|
1053
|
-
const signature = this.layoutSignature(cols, width);
|
|
1054
|
-
if (signature === this.#layoutSignature) {
|
|
1055
|
-
return;
|
|
1056
|
-
}
|
|
1057
|
-
this.#layoutSignature = signature;
|
|
1058
|
-
const res = layoutColumns(cols, width, this.#defaultColWidth);
|
|
1059
|
-
this.#byKey.set(res.byKey);
|
|
1060
|
-
});
|
|
1061
|
-
effect(() => {
|
|
1062
|
-
this.#byKey();
|
|
1063
|
-
const { left, right } = this.#stickySplit();
|
|
1064
|
-
const signature = this.stickySignature(left, right);
|
|
1065
|
-
if (signature === this.#stickySignature) {
|
|
1066
|
-
return;
|
|
1067
|
-
}
|
|
1068
|
-
this.#stickySignature = signature;
|
|
1069
|
-
this.recomputeStickyOffsets(left, right);
|
|
1070
|
-
});
|
|
1071
|
-
}
|
|
1072
|
-
/**
|
|
1073
|
-
* Returns the computed width for a column by its key.
|
|
1074
|
-
*
|
|
1075
|
-
* If the column width has not been calculated, returns the default column width.
|
|
1076
|
-
*
|
|
1077
|
-
* @param key - The unique identifier for the column
|
|
1078
|
-
* @returns Width in pixels
|
|
1079
|
-
*/
|
|
1080
|
-
widthByKey = (key) => this.#byKey()[key] ?? this.#defaultColWidth;
|
|
1081
|
-
/**
|
|
1082
|
-
* Checks if a column is pinned to the left side of the grid.
|
|
1083
|
-
*
|
|
1084
|
-
* @param key - The unique identifier for the column
|
|
1085
|
-
* @returns `true` if the column is sticky on the left, `false` otherwise
|
|
1086
|
-
*/
|
|
1087
|
-
isStickyLeft = (key) => this.#stickyLeftMap.has(key);
|
|
1088
|
-
/**
|
|
1089
|
-
* Checks if a column is pinned to the right side of the grid.
|
|
1090
|
-
*
|
|
1091
|
-
* @param key - The unique identifier for the column
|
|
1092
|
-
* @returns `true` if the column is sticky on the right, `false` otherwise
|
|
1093
|
-
*/
|
|
1094
|
-
isStickyRight = (key) => this.#stickyRightMap.has(key);
|
|
1095
|
-
/**
|
|
1096
|
-
* Returns the horizontal offset for a sticky column.
|
|
1097
|
-
*
|
|
1098
|
-
* Calculates the distance from the specified edge (left or right) where the column
|
|
1099
|
-
* should be positioned. Returns `null` if the column is not sticky in the given direction.
|
|
1100
|
-
*
|
|
1101
|
-
* @param key - The unique identifier for the column
|
|
1102
|
-
* @param dir - The direction to check ('left' or 'right')
|
|
1103
|
-
* @returns Offset in pixels, or `null` if not sticky in the specified direction
|
|
1104
|
-
*/
|
|
1105
|
-
stickyOffset = (key, dir) => dir === 'left' && this.isStickyLeft(key)
|
|
1106
|
-
? (this.#stickyLeftMap.get(key) ?? null)
|
|
1107
|
-
: dir === 'right' && this.isStickyRight(key)
|
|
1108
|
-
? (this.#stickyRightMap.get(key) ?? null)
|
|
1109
|
-
: null;
|
|
1110
|
-
/**
|
|
1111
|
-
* Calculates and updates scrollbar state based on the current scroll position.
|
|
1112
|
-
*
|
|
1113
|
-
* Computes whether the scrollbar should be visible, and if so, determines
|
|
1114
|
-
* the thumb height and position based on scroll height, client height, and scroll position.
|
|
1115
|
-
* Updates the corresponding signal properties with the calculated values.
|
|
1116
|
-
*/
|
|
1117
|
-
calcScrollbar() {
|
|
1118
|
-
const el = this.scrollEl()?.nativeElement;
|
|
1119
|
-
if (!el)
|
|
1120
|
-
return;
|
|
1121
|
-
const state = computeScrollbarState(el.scrollHeight, el.clientHeight, el.scrollTop);
|
|
1122
|
-
if (!state.visible) {
|
|
1123
|
-
this.scrollbarVisible.set(false);
|
|
1124
|
-
this.thumbHeightPx.set(0);
|
|
1125
|
-
this.thumbTopPx.set(0);
|
|
1126
|
-
return;
|
|
1127
|
-
}
|
|
1128
|
-
this.thumbHeightPx.set(state.thumbHeight);
|
|
1129
|
-
this.thumbTopPx.set(state.thumbTop);
|
|
1130
|
-
}
|
|
1131
|
-
recomputeStickyOffsets(leftCols, rightCols) {
|
|
1132
|
-
let acc = 0;
|
|
1133
|
-
this.#stickyLeftMap.clear();
|
|
1134
|
-
for (const col of leftCols) {
|
|
1135
|
-
this.#stickyLeftMap.set(col.key, acc);
|
|
1136
|
-
acc += this.widthByKey(col.key);
|
|
1137
|
-
}
|
|
1138
|
-
acc = 0;
|
|
1139
|
-
this.#stickyRightMap.clear();
|
|
1140
|
-
for (let i = rightCols.length - 1; i >= 0; i--) {
|
|
1141
|
-
const col = rightCols[i];
|
|
1142
|
-
this.#stickyRightMap.set(col.key, acc);
|
|
1143
|
-
acc += this.widthByKey(col.key);
|
|
1144
|
-
}
|
|
1145
|
-
}
|
|
1146
|
-
layoutSignature(cols, width) {
|
|
1147
|
-
const parts = [`w:${width}`];
|
|
1148
|
-
for (const col of cols) {
|
|
1149
|
-
const anyCol = col;
|
|
1150
|
-
const key = String(col.key);
|
|
1151
|
-
const widthVal = anyCol['width'] ?? '';
|
|
1152
|
-
const min = anyCol['minWidth'] ?? '';
|
|
1153
|
-
const max = anyCol['maxWidth'] ?? '';
|
|
1154
|
-
const flex = anyCol['flex'] ?? '';
|
|
1155
|
-
const visible = anyCol['visible'] ?? '';
|
|
1156
|
-
const sticky = anyCol['sticky'] ?? '';
|
|
1157
|
-
parts.push([key, widthVal, min, max, flex, visible, sticky].join('|'));
|
|
1158
|
-
}
|
|
1159
|
-
return parts.join(';');
|
|
1160
|
-
}
|
|
1161
|
-
stickySignature(leftCols, rightCols) {
|
|
1162
|
-
const left = leftCols.map((col) => `${col.key}:${this.widthByKey(col.key)}`).join(',');
|
|
1163
|
-
const right = rightCols.map((col) => `${col.key}:${this.widthByKey(col.key)}`).join(',');
|
|
1164
|
-
return `l:${left}|r:${right}`;
|
|
1165
|
-
}
|
|
1166
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.1", ngImport: i0, type: DataGridVm, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
1167
|
-
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.1.1", ngImport: i0, type: DataGridVm });
|
|
1168
|
-
}
|
|
1169
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.1", ngImport: i0, type: DataGridVm, decorators: [{
|
|
1170
|
-
type: Injectable
|
|
1171
|
-
}], ctorParameters: () => [] });
|
|
1172
|
-
|
|
1173
|
-
// noinspection ES6PreferShortImport
|
|
1174
|
-
/**
|
|
1175
|
-
* Component for rendering individual data grid cells.
|
|
1176
|
-
*
|
|
1177
|
-
* Handles different cell rendering strategies based on column configuration:
|
|
1178
|
-
* - Custom templates via `renderTemplate`
|
|
1179
|
-
* - Global type-specific templates registered in DataGridVm
|
|
1180
|
-
* - Built-in formatters for dates and numbers
|
|
1181
|
-
* - Index display (1-based row numbering)
|
|
1182
|
-
* - Plain text for simple values
|
|
1183
|
-
*
|
|
1184
|
-
* The component automatically determines the appropriate rendering method
|
|
1185
|
-
* based on column properties and applies Angular pipes when needed.
|
|
1186
|
-
*
|
|
1187
|
-
* @template Data - Type of data objects in the grid row
|
|
1188
|
-
*
|
|
1189
|
-
* @example
|
|
1190
|
-
* ```html
|
|
1191
|
-
* <re-data-grid-cell
|
|
1192
|
-
* [index]="0"
|
|
1193
|
-
* [item]="rowData"
|
|
1194
|
-
* [column]="columnConfig"
|
|
1195
|
-
* />
|
|
1196
|
-
* ```
|
|
1197
|
-
*/
|
|
1198
|
-
class DataGridCellComponent {
|
|
1199
|
-
/**
|
|
1200
|
-
* Zero-based index of the current row in the data grid.
|
|
1201
|
-
*
|
|
1202
|
-
* Used for rendering row numbers (displayed as 1-based in 'index' type columns)
|
|
1203
|
-
* and passed to custom templates as context.
|
|
1204
|
-
*/
|
|
1205
|
-
index = input.required(...(ngDevMode ? [{ debugName: "index" }] : []));
|
|
1206
|
-
/**
|
|
1207
|
-
* Data object for the current row.
|
|
1208
|
-
*
|
|
1209
|
-
* Contains the complete row data that can be accessed by column value functions
|
|
1210
|
-
* or custom render templates. Type-safe, according to the Data generic parameter.
|
|
1211
|
-
*/
|
|
1212
|
-
item = input.required(...(ngDevMode ? [{ debugName: "item" }] : []));
|
|
1213
|
-
/**
|
|
1214
|
-
* Column configuration object.
|
|
1215
|
-
*
|
|
1216
|
-
* Defines how the cell should be rendered, including key mapping, type,
|
|
1217
|
-
* custom templates, or value transformation functions.
|
|
1218
|
-
*/
|
|
1219
|
-
column = input.required(...(ngDevMode ? [{ debugName: "column" }] : []));
|
|
1220
|
-
/**
|
|
1221
|
-
* Injected DataGridVm service instance.
|
|
1222
|
-
*
|
|
1223
|
-
* Provides access to global type-specific cell templates registered
|
|
1224
|
-
* at the grid level via the ` globalTypeCellTpls ` map.
|
|
1225
|
-
*/
|
|
1226
|
-
vm = inject(DataGridVm);
|
|
1227
|
-
/**
|
|
1228
|
-
* Computed rendering strategy for the current cell.
|
|
1229
|
-
*
|
|
1230
|
-
* Determines which template block to use based on column configuration:
|
|
1231
|
-
* - `'tpl'` - Custom column template (`renderTemplate` property exists)
|
|
1232
|
-
* - `'globalTypeTpl'` - Global type template (registered in DataGridVm)
|
|
1233
|
-
* - `'date'`, `'number'`, `'index'` - Built-in formatters
|
|
1234
|
-
* - `'plain'` - Default text rendering
|
|
1235
|
-
*
|
|
1236
|
-
* The value is reactive and updates when the column configuration changes.
|
|
1237
|
-
*/
|
|
1238
|
-
type = computed(() => {
|
|
1239
|
-
const col = this.column();
|
|
1240
|
-
if ('renderTemplate' in col) {
|
|
1241
|
-
return 'tpl';
|
|
1242
|
-
}
|
|
1243
|
-
if ('type' in col && this.vm.globalTypeCellTpls.has(col.type)) {
|
|
1244
|
-
return 'globalTypeTpl';
|
|
1245
|
-
}
|
|
1246
|
-
if (this.vm.globalDataCellTpls.has(col.key)) {
|
|
1247
|
-
return 'globalDataTpl';
|
|
1248
|
-
}
|
|
1249
|
-
if (this.vm.globalRowCellTpls.has(col.key)) {
|
|
1250
|
-
return 'globalRowTpl';
|
|
1251
|
-
}
|
|
1252
|
-
return 'type' in col ? col['type'] : 'plain';
|
|
1253
|
-
}, ...(ngDevMode ? [{ debugName: "type" }] : []));
|
|
1254
|
-
/**
|
|
1255
|
-
* Computed cell value extracted from row data.
|
|
1256
|
-
*
|
|
1257
|
-
* Returns the cell's display value by either:
|
|
1258
|
-
* - Calling the `value` function from column configuration (if defined)
|
|
1259
|
-
* - Directly accessing the row property using the column's `key`
|
|
1260
|
-
*
|
|
1261
|
-
* The value is reactive and updates when row data or column configuration changes.
|
|
1262
|
-
* Used as input for templates, pipes, and default text rendering.
|
|
1263
|
-
*/
|
|
1264
|
-
value = computed(() => {
|
|
1265
|
-
const col = this.column();
|
|
1266
|
-
const row = this.item();
|
|
1267
|
-
const rawValue = 'value' in col ? col.value(row) : row[col.key];
|
|
1268
|
-
if ('value' in col || 'renderTemplate' in col) {
|
|
1269
|
-
return rawValue;
|
|
1270
|
-
}
|
|
1271
|
-
if (rawValue === null || rawValue === undefined) {
|
|
1272
|
-
return col.defaultValue ?? rawValue;
|
|
1273
|
-
}
|
|
1274
|
-
return rawValue;
|
|
1275
|
-
}, ...(ngDevMode ? [{ debugName: "value" }] : []));
|
|
1276
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.1", ngImport: i0, type: DataGridCellComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
1277
|
-
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: `
|
|
1278
|
-
@let row = item();
|
|
1279
|
-
@let col = $any(column());
|
|
1280
|
-
@let val = value();
|
|
1281
|
-
|
|
1282
|
-
@switch (type()) {
|
|
1283
|
-
@case ('tpl') {
|
|
1284
|
-
<ng-container
|
|
1285
|
-
[ngTemplateOutlet]="col.renderTemplate"
|
|
1286
|
-
[ngTemplateOutletContext]="{ $implicit: val, value: val, row, col, index: index() }"
|
|
1287
|
-
/>
|
|
1288
|
-
}
|
|
1289
|
-
@case ('globalTypeTpl') {
|
|
1290
|
-
<ng-container
|
|
1291
|
-
[ngTemplateOutlet]="vm.globalTypeCellTpls.get(col.type)"
|
|
1292
|
-
[ngTemplateOutletContext]="{ $implicit: val, value: val, row, col }"
|
|
1293
|
-
/>
|
|
1294
|
-
}
|
|
1295
|
-
@case ('globalDataTpl') {
|
|
1296
|
-
<ng-container
|
|
1297
|
-
[ngTemplateOutlet]="vm.globalDataCellTpls.get(col.key)"
|
|
1298
|
-
[ngTemplateOutletContext]="{ $implicit: val, value: val, row, col }"
|
|
1299
|
-
/>
|
|
1300
|
-
}
|
|
1301
|
-
@case ('globalRowTpl') {
|
|
1302
|
-
<ng-container
|
|
1303
|
-
[ngTemplateOutlet]="vm.globalRowCellTpls.get(col.key)"
|
|
1304
|
-
[ngTemplateOutletContext]="{ $implicit: val, value: val, row, col, index: index() }"
|
|
1305
|
-
/>
|
|
1306
|
-
}
|
|
1307
|
-
@case ('date') {
|
|
1308
|
-
{{ val | date: col?.typeParams }}
|
|
1309
|
-
}
|
|
1310
|
-
@case ('number') {
|
|
1311
|
-
{{ val | number: col?.typeParams }}
|
|
1312
|
-
}
|
|
1313
|
-
@case ('index') {
|
|
1314
|
-
{{ index() + 1 }}
|
|
1315
|
-
}
|
|
1316
|
-
@default {
|
|
1317
|
-
{{ val }}
|
|
1318
|
-
}
|
|
1319
|
-
}
|
|
1320
|
-
`, isInline: true, styles: [":host{width:100%;text-align:inherit}\n"], dependencies: [{ kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "pipe", type: DatePipe, name: "date" }, { kind: "pipe", type: DecimalPipe, name: "number" }] });
|
|
1321
|
-
}
|
|
1322
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.1", ngImport: i0, type: DataGridCellComponent, decorators: [{
|
|
1323
|
-
type: Component,
|
|
1324
|
-
args: [{ selector: 're-data-grid-cell', template: `
|
|
1325
|
-
@let row = item();
|
|
1326
|
-
@let col = $any(column());
|
|
1327
|
-
@let val = value();
|
|
1328
|
-
|
|
1329
|
-
@switch (type()) {
|
|
1330
|
-
@case ('tpl') {
|
|
1331
|
-
<ng-container
|
|
1332
|
-
[ngTemplateOutlet]="col.renderTemplate"
|
|
1333
|
-
[ngTemplateOutletContext]="{ $implicit: val, value: val, row, col, index: index() }"
|
|
1334
|
-
/>
|
|
1335
|
-
}
|
|
1336
|
-
@case ('globalTypeTpl') {
|
|
1337
|
-
<ng-container
|
|
1338
|
-
[ngTemplateOutlet]="vm.globalTypeCellTpls.get(col.type)"
|
|
1339
|
-
[ngTemplateOutletContext]="{ $implicit: val, value: val, row, col }"
|
|
1340
|
-
/>
|
|
1341
|
-
}
|
|
1342
|
-
@case ('globalDataTpl') {
|
|
1343
|
-
<ng-container
|
|
1344
|
-
[ngTemplateOutlet]="vm.globalDataCellTpls.get(col.key)"
|
|
1345
|
-
[ngTemplateOutletContext]="{ $implicit: val, value: val, row, col }"
|
|
1346
|
-
/>
|
|
1347
|
-
}
|
|
1348
|
-
@case ('globalRowTpl') {
|
|
1349
|
-
<ng-container
|
|
1350
|
-
[ngTemplateOutlet]="vm.globalRowCellTpls.get(col.key)"
|
|
1351
|
-
[ngTemplateOutletContext]="{ $implicit: val, value: val, row, col, index: index() }"
|
|
1352
|
-
/>
|
|
1353
|
-
}
|
|
1354
|
-
@case ('date') {
|
|
1355
|
-
{{ val | date: col?.typeParams }}
|
|
1356
|
-
}
|
|
1357
|
-
@case ('number') {
|
|
1358
|
-
{{ val | number: col?.typeParams }}
|
|
1359
|
-
}
|
|
1360
|
-
@case ('index') {
|
|
1361
|
-
{{ index() + 1 }}
|
|
1362
|
-
}
|
|
1363
|
-
@default {
|
|
1364
|
-
{{ val }}
|
|
1365
|
-
}
|
|
1366
|
-
}
|
|
1367
|
-
`, imports: [NgTemplateOutlet, DatePipe, DecimalPipe], styles: [":host{width:100%;text-align:inherit}\n"] }]
|
|
1368
|
-
}], 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 }] }] } });
|
|
1369
|
-
|
|
1370
|
-
const COLUMN_STATE_PRIORITY_KEYS = new Set(['disabled', 'visible', 'sticky']);
|
|
1371
|
-
function mergeDeclarativeColumns(declarative, columns) {
|
|
1372
|
-
if (!declarative.length) {
|
|
1373
|
-
return columns;
|
|
1374
|
-
}
|
|
1375
|
-
if (!columns.length) {
|
|
1376
|
-
return declarative;
|
|
1377
|
-
}
|
|
1378
|
-
const declarativeByKey = new Map(declarative.map((column) => [column.key, column]));
|
|
1379
|
-
const usedKeys = new Set();
|
|
1380
|
-
const merged = [];
|
|
1381
|
-
for (const column of columns) {
|
|
1382
|
-
const base = declarativeByKey.get(column.key);
|
|
1383
|
-
if (!base) {
|
|
1384
|
-
merged.push(column);
|
|
1385
|
-
continue;
|
|
1386
|
-
}
|
|
1387
|
-
usedKeys.add(column.key);
|
|
1388
|
-
merged.push(mergeColumnState(base, column));
|
|
1389
|
-
}
|
|
1390
|
-
for (const column of declarative) {
|
|
1391
|
-
if (!usedKeys.has(column.key)) {
|
|
1392
|
-
merged.push(column);
|
|
1393
|
-
}
|
|
1394
|
-
}
|
|
1395
|
-
return merged;
|
|
1396
|
-
}
|
|
1397
|
-
function mergeColumnState(declarative, state) {
|
|
1398
|
-
const merged = { ...declarative };
|
|
1399
|
-
const stateValue = state;
|
|
1400
|
-
for (const key of Object.keys(stateValue)) {
|
|
1401
|
-
if (key === 'key') {
|
|
1402
|
-
continue;
|
|
1403
|
-
}
|
|
1404
|
-
const value = stateValue[key];
|
|
1405
|
-
if (COLUMN_STATE_PRIORITY_KEYS.has(key)) {
|
|
1406
|
-
if (value !== undefined) {
|
|
1407
|
-
merged[key] = value;
|
|
1408
|
-
}
|
|
1409
|
-
continue;
|
|
1410
|
-
}
|
|
1411
|
-
if (merged[key] === undefined && value !== undefined) {
|
|
1412
|
-
merged[key] = value;
|
|
1413
|
-
}
|
|
1414
|
-
}
|
|
1415
|
-
return merged;
|
|
1416
|
-
}
|
|
1417
|
-
|
|
1418
|
-
// noinspection ES6PreferShortImport
|
|
1419
|
-
function normalizeDeclarativeColumns(defs = [], rowKey) {
|
|
1420
|
-
const columns = [];
|
|
1421
|
-
const rowCellTemplatesByKey = new Map();
|
|
1422
|
-
for (const def of defs) {
|
|
1423
|
-
const key = def.key;
|
|
1424
|
-
const base = {
|
|
1425
|
-
key,
|
|
1426
|
-
header: def.header ?? String(key),
|
|
1427
|
-
headerTemplate: def.headerTemplate,
|
|
1428
|
-
align: def.align,
|
|
1429
|
-
sortKey: def.sortKey,
|
|
1430
|
-
width: def.width,
|
|
1431
|
-
minWidth: def.minWidth,
|
|
1432
|
-
maxWidth: def.maxWidth,
|
|
1433
|
-
flex: def.flex,
|
|
1434
|
-
disabled: def.disabled,
|
|
1435
|
-
visible: def.visible,
|
|
1436
|
-
sticky: def.sticky,
|
|
1437
|
-
tooltip: def.tooltip,
|
|
1438
|
-
expandBy: def.expandBy,
|
|
1439
|
-
};
|
|
1440
|
-
if (def.cellTemplate) {
|
|
1441
|
-
rowCellTemplatesByKey.set(String(key), def.cellTemplate);
|
|
1442
|
-
}
|
|
1443
|
-
if (def.value) {
|
|
1444
|
-
columns.push({
|
|
1445
|
-
...base,
|
|
1446
|
-
value: def.value,
|
|
1447
|
-
track: def.track ?? createDefaultTrack(key, rowKey),
|
|
1448
|
-
});
|
|
1449
|
-
continue;
|
|
1450
|
-
}
|
|
1451
|
-
columns.push({
|
|
1452
|
-
...base,
|
|
1453
|
-
type: def.type ?? 'plain',
|
|
1454
|
-
typeParams: def.typeParams,
|
|
1455
|
-
defaultValue: def.defaultValue,
|
|
1456
|
-
});
|
|
1457
|
-
}
|
|
1458
|
-
return { columns, rowCellTemplatesByKey };
|
|
1459
|
-
}
|
|
1460
|
-
function createDefaultTrack(key, rowKey) {
|
|
1461
|
-
return (row) => {
|
|
1462
|
-
if (!rowKey) {
|
|
1463
|
-
return String(row[key]);
|
|
1464
|
-
}
|
|
1465
|
-
if (typeof rowKey === 'function') {
|
|
1466
|
-
return String(rowKey(row));
|
|
1467
|
-
}
|
|
1468
|
-
return String(row[rowKey]);
|
|
1469
|
-
};
|
|
1470
|
-
}
|
|
1471
|
-
|
|
1472
|
-
/**
|
|
1473
|
-
* Service class for managing row selection in a data grid.
|
|
1474
|
-
*
|
|
1475
|
-
* Handles selection state and operations for grid rows, supporting multiple selection modes
|
|
1476
|
-
* (none, single, multi). Provides reactive signals for tracking selected items and
|
|
1477
|
-
* computed values for selection state.
|
|
1478
|
-
*
|
|
1479
|
-
* @template Data - The type of data objects in the grid, must extend `AnyDict`.
|
|
1480
|
-
*
|
|
1481
|
-
* @example
|
|
1482
|
-
* ```typescript
|
|
1483
|
-
* const selector = new Selector<User>();
|
|
1484
|
-
* selector.data.set(users);
|
|
1485
|
-
* selector.selection.set({ mode: 'multi', key: 'id' });
|
|
1486
|
-
* selector.select(users[0]);
|
|
1487
|
-
* console.log(selector.selectedKeys()); // ['user-id']
|
|
1488
|
-
* ```
|
|
1489
|
-
*/
|
|
1490
|
-
class Selector {
|
|
1491
|
-
/**
|
|
1492
|
-
* Signal containing the full dataset of grid rows.
|
|
1493
|
-
*
|
|
1494
|
-
* This signal holds all data items that can be selected.
|
|
1495
|
-
* Defaults to an empty array.
|
|
1496
|
-
*/
|
|
1497
|
-
data = signal([], ...(ngDevMode ? [{ debugName: "data" }] : []));
|
|
1498
|
-
/**
|
|
1499
|
-
* Signal containing the current selection configuration.
|
|
1500
|
-
*
|
|
1501
|
-
* Defines the selection mode and the key property used for identifying rows.
|
|
1502
|
-
* Defaults to `{ mode: 'none' }` which disables selection.
|
|
1503
|
-
*/
|
|
1504
|
-
selection = signal({ mode: 'none' }, ...(ngDevMode ? [{ debugName: "selection" }] : []));
|
|
1505
|
-
/**
|
|
1506
|
-
* Signal containing the array of currently selected row keys.
|
|
1507
|
-
*
|
|
1508
|
-
* Stores the keys of all selected rows based on the key property
|
|
1509
|
-
* defined in the selection configuration.
|
|
1510
|
-
* Defaults to an empty array.
|
|
1511
|
-
*/
|
|
1512
|
-
selectedKeys = signal([], ...(ngDevMode ? [{ debugName: "selectedKeys" }] : []));
|
|
1513
|
-
/**
|
|
1514
|
-
* Computed signal indicating the overall selection state of all rows.
|
|
1515
|
-
*
|
|
1516
|
-
* Returns:
|
|
1517
|
-
* - `true` if all rows are selected
|
|
1518
|
-
* - `false` if no rows are selected
|
|
1519
|
-
* - `'mixed'` if some but not all rows are selected
|
|
1520
|
-
*
|
|
1521
|
-
* Useful for implementing the "select all" checkbox with an indeterminate state.
|
|
1522
|
-
*/
|
|
1523
|
-
isAllSelected = computed(() => {
|
|
1524
|
-
const selectedCount = this.selectedKeys().length;
|
|
1525
|
-
return selectedCount === this.data().length ? true : selectedCount === 0 ? false : 'mixed';
|
|
1526
|
-
}, ...(ngDevMode ? [{ debugName: "isAllSelected" }] : []));
|
|
1527
|
-
/**
|
|
1528
|
-
* Checks whether a specific row is currently selected.
|
|
1529
|
-
*
|
|
1530
|
-
* Compares the row's key value against the list of selected keys.
|
|
1531
|
-
* Returns `false` if the selection mode is 'none' or the key is not configured.
|
|
1532
|
-
*
|
|
1533
|
-
* @param row - The data row to check selection status for.
|
|
1534
|
-
* @returns `true` if the row is selected, `false` otherwise.
|
|
1535
|
-
*/
|
|
1536
|
-
isSelected(row) {
|
|
1537
|
-
const selection = this.selection();
|
|
1538
|
-
const selected = this.selectedKeys();
|
|
1539
|
-
return 'key' in selection ? selected.includes(row[selection.key]) : false;
|
|
1540
|
-
}
|
|
1541
|
-
/**
|
|
1542
|
-
* Toggles selection of all rows.
|
|
1543
|
-
*
|
|
1544
|
-
* If all rows are selected, deselects all.
|
|
1545
|
-
* If no rows or some rows are selected, selects all.
|
|
1546
|
-
*
|
|
1547
|
-
* This method only works in `'multi'` selection mode and throws an error
|
|
1548
|
-
* if called in any other mode.
|
|
1549
|
-
*
|
|
1550
|
-
* @returns The updated array of selected keys.
|
|
1551
|
-
* @throws {Error} If selection mode is not `'multi'`.
|
|
1552
|
-
*/
|
|
1553
|
-
selectAll() {
|
|
1554
|
-
if (this.selection().mode !== 'multi') {
|
|
1555
|
-
throw new Error('Cannot select all in not "multi" mode');
|
|
1556
|
-
}
|
|
1557
|
-
const selection = this.selection();
|
|
1558
|
-
const state = this.isAllSelected();
|
|
1559
|
-
const nextState = state === false || state === 'mixed';
|
|
1560
|
-
this.selectedKeys.set(!nextState ? [] : this.data().map((row) => row[selection.key]));
|
|
1561
|
-
return this.selectedKeys();
|
|
1562
|
-
}
|
|
1563
|
-
/**
|
|
1564
|
-
* Selects or deselects a specific row.
|
|
1565
|
-
*
|
|
1566
|
-
* Behavior depends on the selection mode:
|
|
1567
|
-
* - In `'single'` mode: replaces current selection with the specified row.
|
|
1568
|
-
* - In `'multi'` mode: toggles the row's selection state (adds if not selected, removes if selected).
|
|
1569
|
-
* - In `'none'` mode: throws an error.
|
|
1570
|
-
*
|
|
1571
|
-
* @param row - The data row to select or deselect.
|
|
1572
|
-
* @returns The updated array of selected keys after the operation.
|
|
1573
|
-
* @throws {Error} If selection mode is `'none'`.
|
|
1574
|
-
*/
|
|
1575
|
-
select(row) {
|
|
1576
|
-
if (this.selection().mode === 'none') {
|
|
1577
|
-
throw new Error('Cannot select row in "none" mode');
|
|
1578
|
-
}
|
|
1579
|
-
const selection = this.selection();
|
|
1580
|
-
if (this.selection().mode === 'single') {
|
|
1581
|
-
const selected = [row[selection.key]];
|
|
1582
|
-
this.selectedKeys.set(selected);
|
|
1583
|
-
return selected;
|
|
1584
|
-
}
|
|
1585
|
-
else {
|
|
1586
|
-
const selectedKeys = this.selectedKeys();
|
|
1587
|
-
const has = selectedKeys.some((it) => it === row[selection.key]);
|
|
1588
|
-
const selected = has
|
|
1589
|
-
? selectedKeys.filter((it) => it !== row[selection.key])
|
|
1590
|
-
: [...selectedKeys, row[selection.key]];
|
|
1591
|
-
this.selectedKeys.set(selected);
|
|
1592
|
-
return selected;
|
|
1593
|
-
}
|
|
1594
|
-
}
|
|
1595
|
-
}
|
|
1596
|
-
|
|
1597
|
-
// noinspection CssUnresolvedCustomProperty
|
|
1598
|
-
class CheckboxIcon {
|
|
1599
|
-
state = input(false, ...(ngDevMode ? [{ debugName: "state" }] : []));
|
|
1600
|
-
disabled = input(false, ...(ngDevMode ? [{ debugName: "disabled" }] : []));
|
|
1601
|
-
boxRef = viewChild((ElementRef), ...(ngDevMode ? [{ debugName: "boxRef" }] : []));
|
|
1602
|
-
constructor() {
|
|
1603
|
-
effect(() => {
|
|
1604
|
-
const el = this.boxRef()?.nativeElement;
|
|
1605
|
-
if (!el) {
|
|
1606
|
-
return;
|
|
1607
|
-
}
|
|
1608
|
-
const state = this.state();
|
|
1609
|
-
if (state === 'mixed') {
|
|
1610
|
-
el.indeterminate = true;
|
|
1611
|
-
el.checked = false;
|
|
1612
|
-
el.dataset.indeterminate = 'true';
|
|
1613
|
-
}
|
|
1614
|
-
else if (state) {
|
|
1615
|
-
el.indeterminate = false;
|
|
1616
|
-
el.checked = true;
|
|
1617
|
-
el.dataset.indeterminate = 'false';
|
|
1618
|
-
}
|
|
1619
|
-
else {
|
|
1620
|
-
el.indeterminate = false;
|
|
1621
|
-
el.checked = false;
|
|
1622
|
-
el.dataset.indeterminate = 'false';
|
|
1623
|
-
}
|
|
1624
|
-
});
|
|
1625
|
-
}
|
|
1626
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.1", ngImport: i0, type: CheckboxIcon, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
1627
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.2.0", version: "21.1.1", type: CheckboxIcon, isStandalone: true, selector: "re-checkbox-ic", inputs: { state: { classPropertyName: "state", publicName: "state", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null } }, viewQueries: [{ propertyName: "boxRef", first: true, predicate: (ElementRef), descendants: true, isSignal: true }], ngImport: i0, template: `
|
|
1628
|
-
<label class="cb" [class.cb--disabled]="disabled()">
|
|
1629
|
-
<input
|
|
1630
|
-
#box
|
|
1631
|
-
class="cb__input"
|
|
1632
|
-
type="checkbox"
|
|
1633
|
-
aria-hidden="true"
|
|
1634
|
-
[disabled]="disabled()"
|
|
1635
|
-
[checked]="state() === true"
|
|
1636
|
-
[attr.data-indeterminate]="state() === 'mixed' ? 'true' : 'false'"
|
|
1637
|
-
(click)="$event.stopPropagation()"
|
|
1638
|
-
/>
|
|
1639
|
-
<span class="cb__box" aria-hidden="true"></span>
|
|
1640
|
-
</label>
|
|
1641
|
-
`, isInline: true, styles: [":host{--re-data-grid-checkbox-size: 20px;--re-data-grid-checkbox-stroke: 2px;--re-data-grid-checkbox-border: var(--border-color, #9aa3af);--re-data-grid-checkbox-tick: var(--surface-neutral, #fff);--re-data-grid-checkbox-surface: var(--surface-neutral, #fff);--re-data-grid-checkbox-active-color: var(--primary-color, #2563eb);display:inline-block;width:var(--re-data-grid-checkbox-size);height:var(--re-data-grid-checkbox-size);-webkit-user-select:none;user-select:none}.cb{cursor:default;display:inline-block}.cb--disabled{opacity:.6}.cb__input{position:absolute;width:0;height:0;opacity:0;pointer-events:none}.cb__box{position:relative;display:inline-block;width:var(--re-data-grid-checkbox-size);height:var(--re-data-grid-checkbox-size);border:var(--re-data-grid-checkbox-stroke) solid var(--re-data-grid-checkbox-border);border-radius:4px;background:var(--re-data-grid-checkbox-surface);transition:background .25s,border-color .25s}.cb__input:checked+.cb__box{border-color:var(--re-data-grid-checkbox-active-color);background:var(--re-data-grid-checkbox-active-color)}.cb__input:checked+.cb__box:after{content:\"\";position:absolute;inset:0;margin:auto;width:10px;height:6px;border:2px solid var(--re-data-grid-checkbox-tick);border-top:0;border-right:0;transform:rotate(-45deg) translate(1px,-1px) scale(.8);opacity:0;animation:tick .25s forwards ease}.cb__input[data-indeterminate=true]+.cb__box{background:var(--re-data-grid-checkbox-active-color);border-color:var(--re-data-grid-checkbox-active-color)}.cb__input[data-indeterminate=true]+.cb__box:after{content:\"\";position:absolute;left:3px;right:3px;top:50%;border-top:2px solid var(--re-data-grid-checkbox-tick);transform:translateY(-50%) scaleX(.3);opacity:0;animation:dash .25s forwards ease}@keyframes tick{to{opacity:1;transform:rotate(-45deg) translate(1px,-1px) scale(1)}}@keyframes dash{to{opacity:1;transform:translateY(-50%) scaleX(1)}}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
1642
|
-
}
|
|
1643
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.1", ngImport: i0, type: CheckboxIcon, decorators: [{
|
|
1644
|
-
type: Component,
|
|
1645
|
-
args: [{ selector: 're-checkbox-ic', standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, template: `
|
|
1646
|
-
<label class="cb" [class.cb--disabled]="disabled()">
|
|
1647
|
-
<input
|
|
1648
|
-
#box
|
|
1649
|
-
class="cb__input"
|
|
1650
|
-
type="checkbox"
|
|
1651
|
-
aria-hidden="true"
|
|
1652
|
-
[disabled]="disabled()"
|
|
1653
|
-
[checked]="state() === true"
|
|
1654
|
-
[attr.data-indeterminate]="state() === 'mixed' ? 'true' : 'false'"
|
|
1655
|
-
(click)="$event.stopPropagation()"
|
|
1656
|
-
/>
|
|
1657
|
-
<span class="cb__box" aria-hidden="true"></span>
|
|
1658
|
-
</label>
|
|
1659
|
-
`, styles: [":host{--re-data-grid-checkbox-size: 20px;--re-data-grid-checkbox-stroke: 2px;--re-data-grid-checkbox-border: var(--border-color, #9aa3af);--re-data-grid-checkbox-tick: var(--surface-neutral, #fff);--re-data-grid-checkbox-surface: var(--surface-neutral, #fff);--re-data-grid-checkbox-active-color: var(--primary-color, #2563eb);display:inline-block;width:var(--re-data-grid-checkbox-size);height:var(--re-data-grid-checkbox-size);-webkit-user-select:none;user-select:none}.cb{cursor:default;display:inline-block}.cb--disabled{opacity:.6}.cb__input{position:absolute;width:0;height:0;opacity:0;pointer-events:none}.cb__box{position:relative;display:inline-block;width:var(--re-data-grid-checkbox-size);height:var(--re-data-grid-checkbox-size);border:var(--re-data-grid-checkbox-stroke) solid var(--re-data-grid-checkbox-border);border-radius:4px;background:var(--re-data-grid-checkbox-surface);transition:background .25s,border-color .25s}.cb__input:checked+.cb__box{border-color:var(--re-data-grid-checkbox-active-color);background:var(--re-data-grid-checkbox-active-color)}.cb__input:checked+.cb__box:after{content:\"\";position:absolute;inset:0;margin:auto;width:10px;height:6px;border:2px solid var(--re-data-grid-checkbox-tick);border-top:0;border-right:0;transform:rotate(-45deg) translate(1px,-1px) scale(.8);opacity:0;animation:tick .25s forwards ease}.cb__input[data-indeterminate=true]+.cb__box{background:var(--re-data-grid-checkbox-active-color);border-color:var(--re-data-grid-checkbox-active-color)}.cb__input[data-indeterminate=true]+.cb__box:after{content:\"\";position:absolute;left:3px;right:3px;top:50%;border-top:2px solid var(--re-data-grid-checkbox-tick);transform:translateY(-50%) scaleX(.3);opacity:0;animation:dash .25s forwards ease}@keyframes tick{to{opacity:1;transform:rotate(-45deg) translate(1px,-1px) scale(1)}}@keyframes dash{to{opacity:1;transform:translateY(-50%) scaleX(1)}}\n"] }]
|
|
1660
|
-
}], ctorParameters: () => [], propDecorators: { state: [{ type: i0.Input, args: [{ isSignal: true, alias: "state", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], boxRef: [{ type: i0.ViewChild, args: [i0.forwardRef(() => ElementRef), { isSignal: true }] }] } });
|
|
1661
|
-
|
|
1662
|
-
/* eslint-disable max-len */
|
|
1663
|
-
class ExpandIcon {
|
|
1664
|
-
expanded = input(false, { ...(ngDevMode ? { debugName: "expanded" } : {}), transform: booleanAttribute });
|
|
1665
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.1", ngImport: i0, type: ExpandIcon, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
1666
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.1.1", type: ExpandIcon, isStandalone: true, selector: "re-expand-ic", inputs: { expanded: { classPropertyName: "expanded", publicName: "expanded", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: `
|
|
1667
|
-
@if (expanded()) {
|
|
1668
|
-
<svg width="16" height="16" viewBox="0 0 16 16">
|
|
1669
|
-
<path
|
|
1670
|
-
d="M15.5 8C15.5 9.48336 15.0601 10.9334 14.236 12.1668C13.4119 13.4001 12.2406 14.3614 10.8701 14.9291C9.49968 15.4968 7.99168 15.6453 6.53683 15.3559C5.08197 15.0665 3.7456 14.3522 2.6967 13.3033C1.64781 12.2544 0.933503 10.918 0.644114 9.46318C0.354725 8.00832 0.50325 6.50032 1.07091 5.12987C1.63856 3.75943 2.59986 2.58809 3.83323 1.76398C5.0666 0.939867 6.51664 0.5 8 0.5C8.98492 0.5 9.96019 0.693993 10.8701 1.0709C11.7801 1.44781 12.6069 2.00026 13.3033 2.6967C13.9997 3.39314 14.5522 4.21993 14.9291 5.12987C15.306 6.03982 15.5 7.01509 15.5 8ZM12.5 8C12.5 7.80109 12.421 7.61032 12.2803 7.46967C12.1397 7.32902 11.9489 7.25 11.75 7.25H4.25C4.05109 7.25 3.86033 7.32902 3.71967 7.46967C3.57902 7.61032 3.5 7.80109 3.5 8C3.5 8.19891 3.57902 8.38968 3.71967 8.53033C3.86033 8.67098 4.05109 8.75 4.25 8.75H11.75C11.9489 8.75 12.1397 8.67098 12.2803 8.53033C12.421 8.38968 12.5 8.19891 12.5 8Z"
|
|
1671
|
-
fill="var(--re-data-grid-expander-color)"
|
|
1672
|
-
/>
|
|
1673
|
-
</svg>
|
|
1674
|
-
} @else {
|
|
1675
|
-
<svg width="16" height="16" viewBox="0 0 16 16">
|
|
1676
|
-
<path
|
|
1677
|
-
d="M8 0.5C6.51664 0.5 5.0666 0.939867 3.83323 1.76398C2.59986 2.58809 1.63856 3.75943 1.07091 5.12987C0.50325 6.50032 0.354725 8.00832 0.644114 9.46318C0.933503 10.918 1.64781 12.2544 2.6967 13.3033C3.7456 14.3522 5.08197 15.0665 6.53683 15.3559C7.99168 15.6453 9.49968 15.4968 10.8701 14.9291C12.2406 14.3614 13.4119 13.4001 14.236 12.1668C15.0601 10.9334 15.5 9.48336 15.5 8C15.5 7.01509 15.306 6.03982 14.9291 5.12987C14.5522 4.21993 13.9997 3.39314 13.3033 2.6967C12.6069 2.00026 11.7801 1.44781 10.8701 1.0709C9.96019 0.693993 8.98492 0.5 8 0.5ZM11.75 8.75H8.75V11.75C8.75 11.9489 8.67099 12.1397 8.53033 12.2803C8.38968 12.421 8.19892 12.5 8 12.5C7.80109 12.5 7.61033 12.421 7.46967 12.2803C7.32902 12.1397 7.25 11.9489 7.25 11.75V8.75H4.25C4.05109 8.75 3.86033 8.67098 3.71967 8.53033C3.57902 8.38968 3.5 8.19891 3.5 8C3.5 7.80109 3.57902 7.61032 3.71967 7.46967C3.86033 7.32902 4.05109 7.25 4.25 7.25H7.25V4.25C7.25 4.05109 7.32902 3.86032 7.46967 3.71967C7.61033 3.57902 7.80109 3.5 8 3.5C8.19892 3.5 8.38968 3.57902 8.53033 3.71967C8.67099 3.86032 8.75 4.05109 8.75 4.25V7.25H11.75C11.9489 7.25 12.1397 7.32902 12.2803 7.46967C12.421 7.61032 12.5 7.80109 12.5 8C12.5 8.19891 12.421 8.38968 12.2803 8.53033C12.1397 8.67098 11.9489 8.75 11.75 8.75Z"
|
|
1678
|
-
fill="var(--re-data-grid-expander-color)"
|
|
1679
|
-
/>
|
|
1680
|
-
</svg>
|
|
1681
|
-
}
|
|
1682
|
-
`, isInline: true, styles: [""] });
|
|
1683
|
-
}
|
|
1684
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.1", ngImport: i0, type: ExpandIcon, decorators: [{
|
|
1685
|
-
type: Component,
|
|
1686
|
-
args: [{ selector: 're-expand-ic', template: `
|
|
1687
|
-
@if (expanded()) {
|
|
1688
|
-
<svg width="16" height="16" viewBox="0 0 16 16">
|
|
1689
|
-
<path
|
|
1690
|
-
d="M15.5 8C15.5 9.48336 15.0601 10.9334 14.236 12.1668C13.4119 13.4001 12.2406 14.3614 10.8701 14.9291C9.49968 15.4968 7.99168 15.6453 6.53683 15.3559C5.08197 15.0665 3.7456 14.3522 2.6967 13.3033C1.64781 12.2544 0.933503 10.918 0.644114 9.46318C0.354725 8.00832 0.50325 6.50032 1.07091 5.12987C1.63856 3.75943 2.59986 2.58809 3.83323 1.76398C5.0666 0.939867 6.51664 0.5 8 0.5C8.98492 0.5 9.96019 0.693993 10.8701 1.0709C11.7801 1.44781 12.6069 2.00026 13.3033 2.6967C13.9997 3.39314 14.5522 4.21993 14.9291 5.12987C15.306 6.03982 15.5 7.01509 15.5 8ZM12.5 8C12.5 7.80109 12.421 7.61032 12.2803 7.46967C12.1397 7.32902 11.9489 7.25 11.75 7.25H4.25C4.05109 7.25 3.86033 7.32902 3.71967 7.46967C3.57902 7.61032 3.5 7.80109 3.5 8C3.5 8.19891 3.57902 8.38968 3.71967 8.53033C3.86033 8.67098 4.05109 8.75 4.25 8.75H11.75C11.9489 8.75 12.1397 8.67098 12.2803 8.53033C12.421 8.38968 12.5 8.19891 12.5 8Z"
|
|
1691
|
-
fill="var(--re-data-grid-expander-color)"
|
|
1692
|
-
/>
|
|
1693
|
-
</svg>
|
|
1694
|
-
} @else {
|
|
1695
|
-
<svg width="16" height="16" viewBox="0 0 16 16">
|
|
1696
|
-
<path
|
|
1697
|
-
d="M8 0.5C6.51664 0.5 5.0666 0.939867 3.83323 1.76398C2.59986 2.58809 1.63856 3.75943 1.07091 5.12987C0.50325 6.50032 0.354725 8.00832 0.644114 9.46318C0.933503 10.918 1.64781 12.2544 2.6967 13.3033C3.7456 14.3522 5.08197 15.0665 6.53683 15.3559C7.99168 15.6453 9.49968 15.4968 10.8701 14.9291C12.2406 14.3614 13.4119 13.4001 14.236 12.1668C15.0601 10.9334 15.5 9.48336 15.5 8C15.5 7.01509 15.306 6.03982 14.9291 5.12987C14.5522 4.21993 13.9997 3.39314 13.3033 2.6967C12.6069 2.00026 11.7801 1.44781 10.8701 1.0709C9.96019 0.693993 8.98492 0.5 8 0.5ZM11.75 8.75H8.75V11.75C8.75 11.9489 8.67099 12.1397 8.53033 12.2803C8.38968 12.421 8.19892 12.5 8 12.5C7.80109 12.5 7.61033 12.421 7.46967 12.2803C7.32902 12.1397 7.25 11.9489 7.25 11.75V8.75H4.25C4.05109 8.75 3.86033 8.67098 3.71967 8.53033C3.57902 8.38968 3.5 8.19891 3.5 8C3.5 7.80109 3.57902 7.61032 3.71967 7.46967C3.86033 7.32902 4.05109 7.25 4.25 7.25H7.25V4.25C7.25 4.05109 7.32902 3.86032 7.46967 3.71967C7.61033 3.57902 7.80109 3.5 8 3.5C8.19892 3.5 8.38968 3.57902 8.53033 3.71967C8.67099 3.86032 8.75 4.05109 8.75 4.25V7.25H11.75C11.9489 7.25 12.1397 7.32902 12.2803 7.46967C12.421 7.61032 12.5 7.80109 12.5 8C12.5 8.19891 12.421 8.38968 12.2803 8.53033C12.1397 8.67098 11.9489 8.75 11.75 8.75Z"
|
|
1698
|
-
fill="var(--re-data-grid-expander-color)"
|
|
1699
|
-
/>
|
|
1700
|
-
</svg>
|
|
1701
|
-
}
|
|
1702
|
-
` }]
|
|
1703
|
-
}], propDecorators: { expanded: [{ type: i0.Input, args: [{ isSignal: true, alias: "expanded", required: false }] }] } });
|
|
1704
|
-
|
|
1705
|
-
/* eslint-disable max-len */
|
|
1706
|
-
class SortIcon {
|
|
1707
|
-
direction = input('asc', ...(ngDevMode ? [{ debugName: "direction" }] : []));
|
|
1708
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.1", ngImport: i0, type: SortIcon, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
1709
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "21.1.1", type: SortIcon, isStandalone: true, selector: "re-sort-ic", inputs: { direction: { classPropertyName: "direction", publicName: "direction", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: `
|
|
1710
|
-
<svg width="18" height="18" viewBox="0 0 18 18" class="{{ direction() }}">
|
|
1711
|
-
<path
|
|
1712
|
-
d="M12.84 8.51259L9.66754 11.6851V4.59009C9.66754 4.39118 9.58852 4.20041 9.44787 4.05976C9.30722 3.91911 9.11645 3.84009 8.91754 3.84009C8.71863 3.84009 8.52786 3.91911 8.38721 4.05976C8.24656 4.20041 8.16754 4.39118 8.16754 4.59009V11.6851L4.99504 8.51259C4.92511 8.44266 4.84209 8.38719 4.75073 8.34934C4.65936 8.3115 4.56144 8.29202 4.46254 8.29202C4.36365 8.29202 4.26572 8.3115 4.17435 8.34934C4.08299 8.38719 3.99997 8.44266 3.93004 8.51259C3.86011 8.58252 3.80464 8.66553 3.7668 8.7569C3.72895 8.84827 3.70947 8.94619 3.70947 9.04509C3.70947 9.14398 3.72895 9.24191 3.7668 9.33327C3.80464 9.42464 3.86011 9.50766 3.93004 9.57759L8.38504 14.0251C8.45349 14.0952 8.53503 14.1513 8.62504 14.1901C8.71842 14.2256 8.81762 14.2435 8.91754 14.2426C9.01505 14.2438 9.11186 14.226 9.20254 14.1901C9.29255 14.1513 9.37409 14.0952 9.44254 14.0251L13.8975 9.57759C13.9678 9.50787 14.0236 9.42491 14.0617 9.33352C14.0998 9.24213 14.1194 9.1441 14.1194 9.04509C14.1194 8.94608 14.0998 8.84805 14.0617 8.75666C14.0236 8.66526 13.9678 8.58231 13.8975 8.51259C13.757 8.3729 13.5669 8.29449 13.3688 8.29449C13.1707 8.29449 12.9806 8.3729 12.84 8.51259Z"
|
|
1713
|
-
fill="currentColor"
|
|
1714
|
-
/>
|
|
1715
|
-
</svg>
|
|
1716
|
-
`, isInline: true, styles: ["svg{transition:transform .3s ease-in-out}svg:not(.asc):not(.desc){display:none}.asc{transform:rotate(-180deg)}.desc{transform:rotate(0)}\n"] });
|
|
1717
|
-
}
|
|
1718
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.1", ngImport: i0, type: SortIcon, decorators: [{
|
|
1719
|
-
type: Component,
|
|
1720
|
-
args: [{ selector: 're-sort-ic', template: `
|
|
1721
|
-
<svg width="18" height="18" viewBox="0 0 18 18" class="{{ direction() }}">
|
|
1722
|
-
<path
|
|
1723
|
-
d="M12.84 8.51259L9.66754 11.6851V4.59009C9.66754 4.39118 9.58852 4.20041 9.44787 4.05976C9.30722 3.91911 9.11645 3.84009 8.91754 3.84009C8.71863 3.84009 8.52786 3.91911 8.38721 4.05976C8.24656 4.20041 8.16754 4.39118 8.16754 4.59009V11.6851L4.99504 8.51259C4.92511 8.44266 4.84209 8.38719 4.75073 8.34934C4.65936 8.3115 4.56144 8.29202 4.46254 8.29202C4.36365 8.29202 4.26572 8.3115 4.17435 8.34934C4.08299 8.38719 3.99997 8.44266 3.93004 8.51259C3.86011 8.58252 3.80464 8.66553 3.7668 8.7569C3.72895 8.84827 3.70947 8.94619 3.70947 9.04509C3.70947 9.14398 3.72895 9.24191 3.7668 9.33327C3.80464 9.42464 3.86011 9.50766 3.93004 9.57759L8.38504 14.0251C8.45349 14.0952 8.53503 14.1513 8.62504 14.1901C8.71842 14.2256 8.81762 14.2435 8.91754 14.2426C9.01505 14.2438 9.11186 14.226 9.20254 14.1901C9.29255 14.1513 9.37409 14.0952 9.44254 14.0251L13.8975 9.57759C13.9678 9.50787 14.0236 9.42491 14.0617 9.33352C14.0998 9.24213 14.1194 9.1441 14.1194 9.04509C14.1194 8.94608 14.0998 8.84805 14.0617 8.75666C14.0236 8.66526 13.9678 8.58231 13.8975 8.51259C13.757 8.3729 13.5669 8.29449 13.3688 8.29449C13.1707 8.29449 12.9806 8.3729 12.84 8.51259Z"
|
|
1724
|
-
fill="currentColor"
|
|
1725
|
-
/>
|
|
1726
|
-
</svg>
|
|
1727
|
-
`, styles: ["svg{transition:transform .3s ease-in-out}svg:not(.asc):not(.desc){display:none}.asc{transform:rotate(-180deg)}.desc{transform:rotate(0)}\n"] }]
|
|
1728
|
-
}], propDecorators: { direction: [{ type: i0.Input, args: [{ isSignal: true, alias: "direction", required: false }] }] } });
|
|
1729
|
-
|
|
1730
|
-
/**
|
|
1731
|
-
* Data grid component with virtual scrolling, sorting, selection, and pagination support.
|
|
1732
|
-
*
|
|
1733
|
-
* Provides high-performance rendering of large datasets with features like:
|
|
1734
|
-
* - Virtual scrolling for efficient DOM management
|
|
1735
|
-
* - Column sorting and expandable columns
|
|
1736
|
-
* - Row selection (single/multiple)
|
|
1737
|
-
* - Pagination modes: none, pagination, or infinite scroll
|
|
1738
|
-
* - Pinned rows
|
|
1739
|
-
* - Custom cell and header templates
|
|
1740
|
-
*
|
|
1741
|
-
* @example
|
|
1742
|
-
* ```html
|
|
1743
|
-
* <re-data-grid
|
|
1744
|
-
* [data]="users"
|
|
1745
|
-
* [columns]="columns"
|
|
1746
|
-
* [selection]="{ mode: 'multiple' }"
|
|
1747
|
-
* (sortChange)="onSort($event)"
|
|
1748
|
-
* />
|
|
1749
|
-
* ```
|
|
1750
|
-
*/
|
|
1751
|
-
class DataGrid {
|
|
1752
|
-
defaults = inject(DATA_GRID_CONFIG) || DEFAULT_DATA_GRID_DEFAULTS;
|
|
1753
|
-
/**
|
|
1754
|
-
* Array of data to display in the table.
|
|
1755
|
-
*
|
|
1756
|
-
* Each item represents a single row. The component will efficiently render
|
|
1757
|
-
* only visible rows using virtual scrolling.
|
|
1758
|
-
*/
|
|
1759
|
-
data = input([], ...(ngDevMode ? [{ debugName: "data" }] : []));
|
|
1760
|
-
/**
|
|
1761
|
-
* Column configuration for the table.
|
|
1762
|
-
*
|
|
1763
|
-
* Defines how each column should be rendered, sorted, and styled.
|
|
1764
|
-
* Supports custom templates, sorting keys, and expandable columns.
|
|
1765
|
-
*/
|
|
1766
|
-
columns = input([], ...(ngDevMode ? [{ debugName: "columns" }] : []));
|
|
1767
|
-
/**
|
|
1768
|
-
* Pagination mode: 'none', 'pagination', or 'infinity'.
|
|
1769
|
-
*
|
|
1770
|
-
* - `none` - No pagination, all data is rendered
|
|
1771
|
-
* - `pagination` - Classic page-based pagination with fixed page size
|
|
1772
|
-
* - `infinity` - Infinite scroll mode, loads more data as user scrolls
|
|
1773
|
-
*/
|
|
1774
|
-
mode = input(this.defaults.mode, ...(ngDevMode ? [{ debugName: "mode" }] : []));
|
|
1775
|
-
/**
|
|
1776
|
-
* Array of pinned rows that remain visible at the top or bottom.
|
|
1777
|
-
*
|
|
1778
|
-
* Pinned rows stay fixed while the rest of the content scrolls.
|
|
1779
|
-
* Useful for totals, summaries, or always-visible items.
|
|
1780
|
-
*/
|
|
1781
|
-
pinnedRows = input([], ...(ngDevMode ? [{ debugName: "pinnedRows" }] : []));
|
|
1782
|
-
/**
|
|
1783
|
-
* Function to determine if a row should become sticky at the top.
|
|
1784
|
-
*
|
|
1785
|
-
* When provided, rows matching this predicate will stick to the top
|
|
1786
|
-
* of the scroll area as the user scrolls.
|
|
1787
|
-
*/
|
|
1788
|
-
isRowSticky = input(undefined, ...(ngDevMode ? [{ debugName: "isRowSticky" }] : []));
|
|
1789
|
-
/**
|
|
1790
|
-
* Function to choose a custom template for a row.
|
|
1791
|
-
*
|
|
1792
|
-
* If it returns a template, the row will be rendered with it;
|
|
1793
|
-
* otherwise, the default row rendering is used.
|
|
1794
|
-
*/
|
|
1795
|
-
getRowTemplate = input(undefined, ...(ngDevMode ? [{ debugName: "getRowTemplate" }] : []));
|
|
1796
|
-
/**
|
|
1797
|
-
* Whether to add an index column showing row numbers.
|
|
1798
|
-
*
|
|
1799
|
-
* When enabled, it automatically adds a column displaying sequential row numbers.
|
|
1800
|
-
*/
|
|
1801
|
-
hasIndexColumn = input(this.defaults.hasIndexColumn, { ...(ngDevMode ? { debugName: "hasIndexColumn" } : {}), transform: booleanAttribute });
|
|
1802
|
-
/**
|
|
1803
|
-
* Row selection configuration.
|
|
1804
|
-
*
|
|
1805
|
-
* Controls whether users can select rows and in what mode:
|
|
1806
|
-
* - `{ mode: 'none' }` - No selection
|
|
1807
|
-
* - `{ mode: 'single' }` - Single row selection
|
|
1808
|
-
* - `{ mode: 'multi' }` - Multiple row selection with checkboxes
|
|
1809
|
-
*
|
|
1810
|
-
* When selection is enabled, a `key` must be provided to identify rows.
|
|
1811
|
-
*/
|
|
1812
|
-
selection = input(this.defaults.selection, ...(ngDevMode ? [{ debugName: "selection" }] : []));
|
|
1813
|
-
/**
|
|
1814
|
-
* Number of items per page.
|
|
1815
|
-
*
|
|
1816
|
-
* Used in pagination and infinity scroll modes to control
|
|
1817
|
-
* how many rows are loaded at once. Default is 20.
|
|
1818
|
-
*/
|
|
1819
|
-
pageSize = input(this.defaults.pageSize, { ...(ngDevMode ? { debugName: "pageSize" } : {}), transform: numberAttribute });
|
|
1820
|
-
/**
|
|
1821
|
-
* Height of each row in pixels.
|
|
1822
|
-
*
|
|
1823
|
-
* Used for virtual scrolling calculations. Must be consistent
|
|
1824
|
-
* across all rows for accurate scrolling. Default is 40.
|
|
1825
|
-
*/
|
|
1826
|
-
rowHeight = input(this.defaults.rowHeight, { ...(ngDevMode ? { debugName: "rowHeight" } : {}), transform: numberAttribute });
|
|
1827
|
-
/**
|
|
1828
|
-
* Grid height configuration.
|
|
1829
|
-
*
|
|
1830
|
-
* - `number` - Fixed height in pixels
|
|
1831
|
-
* - `'full'` - Fill container height (100%), can be managed by host component style
|
|
1832
|
-
* - `'default'` - Height by CSS var (--re-data-grid-height)
|
|
1833
|
-
*/
|
|
1834
|
-
height = input(this.defaults.height, ...(ngDevMode ? [{ debugName: "height" }] : []));
|
|
1835
|
-
/**
|
|
1836
|
-
* Size of the virtual scroll buffer.
|
|
1837
|
-
*
|
|
1838
|
-
* Number of extra rows to render above and below the viewport
|
|
1839
|
-
* to reduce flickering during fast scrolling. Default is 8.
|
|
1840
|
-
*/
|
|
1841
|
-
virtualBuffer = input(this.defaults.virtualBuffer, ...(ngDevMode ? [{ debugName: "virtualBuffer" }] : []));
|
|
1842
|
-
/**
|
|
1843
|
-
* Locks vertical scrolling while keeping horizontal scrolling enabled.
|
|
1844
|
-
*
|
|
1845
|
-
* Useful when the grid height is fixed by the parent and vertical scrolling
|
|
1846
|
-
* should be managed externally.
|
|
1847
|
-
*/
|
|
1848
|
-
lockVerticalScroll = input(false, { ...(ngDevMode ? { debugName: "lockVerticalScroll" } : {}), transform: booleanAttribute });
|
|
1849
|
-
/**
|
|
1850
|
-
* Header group configuration for creating multi-level column headers.
|
|
1851
|
-
*
|
|
1852
|
-
* Allows grouping multiple columns under a common header label.
|
|
1853
|
-
* Each group spans from a starting column to an ending column and can have a custom alignment.
|
|
1854
|
-
*
|
|
1855
|
-
* @example
|
|
1856
|
-
* ```typescript
|
|
1857
|
-
* headerGroups = [
|
|
1858
|
-
* {
|
|
1859
|
-
* key: 'personal-info',
|
|
1860
|
-
* title: 'Personal Information',
|
|
1861
|
-
* from: 'name',
|
|
1862
|
-
* to: 'email',
|
|
1863
|
-
* align: 'center'
|
|
1864
|
-
* }
|
|
1865
|
-
* ];
|
|
1866
|
-
* ```
|
|
1867
|
-
*/
|
|
1868
|
-
headerGroups = input([], ...(ngDevMode ? [{ debugName: "headerGroups" }] : []));
|
|
1869
|
-
/**
|
|
1870
|
-
* Loading state indicator.
|
|
1871
|
-
*
|
|
1872
|
-
* When true, displays loading template instead of data.
|
|
1873
|
-
* Useful during async data fetching operations.
|
|
1874
|
-
*/
|
|
1875
|
-
loading = input(false, { ...(ngDevMode ? { debugName: "loading" } : {}), transform: booleanAttribute });
|
|
1876
|
-
loadingMode = input(this.defaults.loadingMode, ...(ngDevMode ? [{ debugName: "loadingMode" }] : []));
|
|
1877
|
-
deferContent = input(this.defaults.deferContent, { ...(ngDevMode ? { debugName: "deferContent" } : {}), transform: booleanAttribute });
|
|
1878
|
-
deferHeader = input(this.defaults.deferHeader, { ...(ngDevMode ? { debugName: "deferHeader" } : {}), transform: booleanAttribute });
|
|
1879
|
-
deferPinned = input(this.defaults.deferPinned, { ...(ngDevMode ? { debugName: "deferPinned" } : {}), transform: booleanAttribute });
|
|
1880
|
-
deferCells = input(this.defaults.deferCells, { ...(ngDevMode ? { debugName: "deferCells" } : {}), transform: booleanAttribute });
|
|
1881
|
-
deferIcons = input(this.defaults.deferIcons, { ...(ngDevMode ? { debugName: "deferIcons" } : {}), transform: booleanAttribute });
|
|
1882
|
-
deferTooltip = input(this.defaults.deferTooltip, { ...(ngDevMode ? { debugName: "deferTooltip" } : {}), transform: booleanAttribute });
|
|
1883
|
-
/**
|
|
1884
|
-
* Function or property name for getting a unique row key.
|
|
1885
|
-
*
|
|
1886
|
-
* Used for efficient change detection and row tracking.
|
|
1887
|
-
* Can be a property name (string) or a function that returns a unique identifier.
|
|
1888
|
-
*/
|
|
1889
|
-
rowKey = input(undefined, ...(ngDevMode ? [{ debugName: "rowKey" }] : []));
|
|
1890
|
-
/**
|
|
1891
|
-
* Whether to start a page count from 0 (true) or 1 (false).
|
|
1892
|
-
*
|
|
1893
|
-
* Controls the numbering scheme for pagination events.
|
|
1894
|
-
* Default is true (0-based indexing).
|
|
1895
|
-
*/
|
|
1896
|
-
pageStartFromZero = input(this.defaults.pageStartFromZero, { ...(ngDevMode ? { debugName: "pageStartFromZero" } : {}), transform: booleanAttribute });
|
|
1897
|
-
/**
|
|
1898
|
-
* Event emitted when requesting data for a new page.
|
|
1899
|
-
*
|
|
1900
|
-
* Fired in pagination and infinity scroll modes when the user navigates
|
|
1901
|
-
* to a different page or scrolls near the end of current data.
|
|
1902
|
-
*
|
|
1903
|
-
* @example
|
|
1904
|
-
* ```typescript
|
|
1905
|
-
* onPageChange(event: GridPageChangeEvent) {
|
|
1906
|
-
* this.loadData(event.page, event.rows);
|
|
1907
|
-
* }
|
|
1908
|
-
* ```
|
|
1909
|
-
*/
|
|
1910
|
-
pageChange = output();
|
|
1911
|
-
/**
|
|
1912
|
-
* Event emitted when sort order changes.
|
|
1913
|
-
*
|
|
1914
|
-
* Fired when a user clicks on a sortable column header.
|
|
1915
|
-
* Contains the sort key and direction (asc/desc).
|
|
1916
|
-
*
|
|
1917
|
-
* @example
|
|
1918
|
-
* ```typescript
|
|
1919
|
-
* onSortChange(event: GridSortEvent<User>) {
|
|
1920
|
-
* this.data = sortBy(this.data, event.key, event.order);
|
|
1921
|
-
* }
|
|
1922
|
-
* ```
|
|
1923
|
-
*/
|
|
1924
|
-
sortChange = output();
|
|
1925
|
-
/**
|
|
1926
|
-
* Event emitted when selected rows change.
|
|
1927
|
-
*
|
|
1928
|
-
* Fired when a user selects or deselects rows.
|
|
1929
|
-
* Contains an array of currently selected row keys.
|
|
1930
|
-
*/
|
|
1931
|
-
selectChange = output();
|
|
1932
|
-
/**
|
|
1933
|
-
* Event emitted when a row is clicked.
|
|
1934
|
-
*
|
|
1935
|
-
* Contains the clicked row data and its index.
|
|
1936
|
-
*/
|
|
1937
|
-
rowClick = output();
|
|
1938
|
-
/**
|
|
1939
|
-
* Event emitted when a row is right-clicked (context menu).
|
|
1940
|
-
*
|
|
1941
|
-
* Contains the clicked row data, its index, and the native mouse event.
|
|
1942
|
-
*/
|
|
1943
|
-
rowContext = output();
|
|
1944
|
-
/**
|
|
1945
|
-
* Event emitted when a row is double-clicked.
|
|
1946
|
-
*
|
|
1947
|
-
* Contains the clicked row data, its index, and the native mouse event.
|
|
1948
|
-
*/
|
|
1949
|
-
rowDoubleClick = output();
|
|
1950
|
-
/**
|
|
1951
|
-
* Event emitted when a cell is clicked.
|
|
1952
|
-
*
|
|
1953
|
-
* Contains the clicked row, column configuration, and row index.
|
|
1954
|
-
*/
|
|
1955
|
-
cellClick = output();
|
|
1956
|
-
/**
|
|
1957
|
-
* Event emitted when a cell is right-clicked (context menu).
|
|
1958
|
-
*
|
|
1959
|
-
* Contains the clicked row, column configuration, row index, and the native mouse event.
|
|
1960
|
-
*/
|
|
1961
|
-
cellContext = output();
|
|
1962
|
-
/**
|
|
1963
|
-
* Event emitted when a cell is double-clicked.
|
|
1964
|
-
*
|
|
1965
|
-
* Contains the clicked row, column configuration, row index, and the native mouse event.
|
|
1966
|
-
*/
|
|
1967
|
-
cellDoubleClick = output();
|
|
1968
|
-
vm = inject((DataGridVm));
|
|
1969
|
-
selector = new Selector();
|
|
1970
|
-
rootEl = viewChild('root', ...(ngDevMode ? [{ debugName: "rootEl" }] : []));
|
|
1971
|
-
scrollEl = viewChild('scroll', ...(ngDevMode ? [{ debugName: "scrollEl" }] : []));
|
|
1972
|
-
headerEl = viewChild('header', ...(ngDevMode ? [{ debugName: "headerEl" }] : []));
|
|
1973
|
-
ngZone = inject(NgZone);
|
|
1974
|
-
cellTypedSlotRefs = contentChildren(DataGridTypeCellTemplateDirective, ...(ngDevMode ? [{ debugName: "cellTypedSlotRefs" }] : []));
|
|
1975
|
-
cellDataSlotRefs = contentChildren(DataGridCellTemplateDirective, ...(ngDevMode ? [{ debugName: "cellDataSlotRefs" }] : []));
|
|
1976
|
-
declarativeColumnRefs = contentChildren(DataGridDeclarativeColumn, ...(ngDevMode ? [{ debugName: "declarativeColumnRefs" }] : []));
|
|
1977
|
-
headerSlotRefs = contentChildren(DataGridHeaderTemplateDirective, ...(ngDevMode ? [{ debugName: "headerSlotRefs" }] : []));
|
|
1978
|
-
emptySlotRefs = contentChildren(DataGridCellEmptyDirective, ...(ngDevMode ? [{ debugName: "emptySlotRefs" }] : []));
|
|
1979
|
-
loadingSlotRefs = contentChildren(DataGridCellLoadingDirective, ...(ngDevMode ? [{ debugName: "loadingSlotRefs" }] : []));
|
|
1980
|
-
sortIcSlotRefs = contentChildren(DataGridSortIconDirective, ...(ngDevMode ? [{ debugName: "sortIcSlotRefs" }] : []));
|
|
1981
|
-
expanderIcSlotRefs = contentChildren(DataGridExpanderIconDirective, ...(ngDevMode ? [{ debugName: "expanderIcSlotRefs" }] : []));
|
|
1982
|
-
stickyRowSlotRefs = contentChildren(DataGridStickyRowDirective, ...(ngDevMode ? [{ debugName: "stickyRowSlotRefs" }] : []));
|
|
1983
|
-
rowSlotRefs = contentChildren(DataGridRowDirective, ...(ngDevMode ? [{ debugName: "rowSlotRefs" }] : []));
|
|
1984
|
-
emptyTpl = computed(() => this.emptySlotRefs()?.[0], ...(ngDevMode ? [{ debugName: "emptyTpl" }] : []));
|
|
1985
|
-
loadingTpl = computed(() => this.loadingSlotRefs()?.[0], ...(ngDevMode ? [{ debugName: "loadingTpl" }] : []));
|
|
1986
|
-
sortTpl = computed(() => this.sortIcSlotRefs()?.at(0), ...(ngDevMode ? [{ debugName: "sortTpl" }] : []));
|
|
1987
|
-
expanderTpl = computed(() => this.expanderIcSlotRefs()?.at(0), ...(ngDevMode ? [{ debugName: "expanderTpl" }] : []));
|
|
1988
|
-
stickyRowTpl = computed(() => this.stickyRowSlotRefs()?.at(0), ...(ngDevMode ? [{ debugName: "stickyRowTpl" }] : []));
|
|
1989
|
-
rowTpl = computed(() => this.rowSlotRefs()?.at(0), ...(ngDevMode ? [{ debugName: "rowTpl" }] : []));
|
|
1990
|
-
renderSlots = signal([], ...(ngDevMode ? [{ debugName: "renderSlots" }] : []));
|
|
1991
|
-
renderCount = 0;
|
|
1992
|
-
lastStartIndex = -1;
|
|
1993
|
-
lastEndIndex = -1;
|
|
1994
|
-
startIndex = 0;
|
|
1995
|
-
headerHeight = signal(this.defaults.headerHeight, ...(ngDevMode ? [{ debugName: "headerHeight" }] : []));
|
|
1996
|
-
tooltipEl = viewChild('tooltip', ...(ngDevMode ? [{ debugName: "tooltipEl" }] : []));
|
|
1997
|
-
tooltipState = signal({
|
|
1998
|
-
text: '',
|
|
1999
|
-
x: 0,
|
|
2000
|
-
y: 0,
|
|
2001
|
-
visible: false,
|
|
2002
|
-
}, ...(ngDevMode ? [{ debugName: "tooltipState" }] : []));
|
|
2003
|
-
stickyRowIndex = signal(null, ...(ngDevMode ? [{ debugName: "stickyRowIndex" }] : []));
|
|
2004
|
-
stickyRowTopPx = signal(0, ...(ngDevMode ? [{ debugName: "stickyRowTopPx" }] : []));
|
|
2005
|
-
stickyRowData = computed(() => {
|
|
2006
|
-
const index = this.stickyRowIndex();
|
|
2007
|
-
const rows = this.data();
|
|
2008
|
-
if (index === null || index < 0 || index >= rows.length) {
|
|
2009
|
-
return null;
|
|
2010
|
-
}
|
|
2011
|
-
return rows[index];
|
|
2012
|
-
}, ...(ngDevMode ? [{ debugName: "stickyRowData" }] : []));
|
|
2013
|
-
stickyIndexes = signal([], ...(ngDevMode ? [{ debugName: "stickyIndexes" }] : []));
|
|
2014
|
-
expanderMap = signal(new Map(), ...(ngDevMode ? [{ debugName: "expanderMap" }] : []));
|
|
2015
|
-
declarativeColumns = computed(() => normalizeDeclarativeColumns(this.declarativeColumnRefs().map((columnRef) => columnRef.toDeclarativeColumn()), this.rowKey()), ...(ngDevMode ? [{ debugName: "declarativeColumns" }] : []));
|
|
2016
|
-
sourceColumns = computed(() => mergeDeclarativeColumns(this.declarativeColumns().columns, this.columns()), ...(ngDevMode ? [{ debugName: "sourceColumns" }] : []));
|
|
2017
|
-
extendedColumns = computed(() => {
|
|
2018
|
-
const hasSelection = this.selection().mode !== 'none';
|
|
2019
|
-
const hasIndex = this.hasIndexColumn();
|
|
2020
|
-
const newColumns = [];
|
|
2021
|
-
hasSelection && newColumns.push(GRID_CHECKBOX_COLUMN);
|
|
2022
|
-
hasIndex && newColumns.push(GRID_INDEX_COLUMN);
|
|
2023
|
-
return [...newColumns, ...this.sourceColumns()];
|
|
2024
|
-
}, ...(ngDevMode ? [{ debugName: "extendedColumns" }] : []));
|
|
2025
|
-
/**
|
|
2026
|
-
* Computed CSS height value based on height setting.
|
|
2027
|
-
*/
|
|
2028
|
-
styleHeight = computed(() => {
|
|
2029
|
-
const height = this.height();
|
|
2030
|
-
if (typeof height === 'number') {
|
|
2031
|
-
return `${height}px`;
|
|
2032
|
-
}
|
|
2033
|
-
else if (height === 'full') {
|
|
2034
|
-
return '100%';
|
|
2035
|
-
}
|
|
2036
|
-
return 'var(--re-data-grid-height)';
|
|
2037
|
-
}, ...(ngDevMode ? [{ debugName: "styleHeight" }] : []));
|
|
2038
|
-
hideSbTimeout;
|
|
2039
|
-
scrollRafId = null;
|
|
2040
|
-
scrollbarRafId = null;
|
|
2041
|
-
stickyRafId = null;
|
|
2042
|
-
lastInfinityPageRequested = null;
|
|
2043
|
-
lastInfinityTotal = -1;
|
|
2044
|
-
currentSortField;
|
|
2045
|
-
currentSortOrder = 'asc';
|
|
2046
|
-
subscription = new Subscription();
|
|
2047
|
-
observer;
|
|
2048
|
-
resizeSubject = new Subject();
|
|
2049
|
-
lastResizeWidth = -1;
|
|
2050
|
-
lastResizeHeight = -1;
|
|
2051
|
-
constructor() {
|
|
2052
|
-
afterRenderEffect(() => {
|
|
2053
|
-
this.initRefs();
|
|
2054
|
-
this.initHeader();
|
|
2055
|
-
this.initScroll();
|
|
2056
|
-
this.initObserver();
|
|
2057
|
-
});
|
|
2058
|
-
afterRenderEffect(() => this.initSelector());
|
|
2059
|
-
afterRenderEffect(() => {
|
|
2060
|
-
this.initVm();
|
|
2061
|
-
this.initSort();
|
|
2062
|
-
this.initExpander();
|
|
2063
|
-
});
|
|
2064
|
-
effect(() => {
|
|
2065
|
-
this.data();
|
|
2066
|
-
this.rowHeight();
|
|
2067
|
-
this.pinnedRows();
|
|
2068
|
-
this.isRowSticky();
|
|
2069
|
-
this.getRowTemplate();
|
|
2070
|
-
this.loading();
|
|
2071
|
-
this.mode();
|
|
2072
|
-
this.vm.containerWidth();
|
|
2073
|
-
this.onVerticalScroll(true);
|
|
2074
|
-
});
|
|
2075
|
-
effect(() => {
|
|
2076
|
-
const total = this.data()?.length ?? 0;
|
|
2077
|
-
if (total !== this.lastInfinityTotal) {
|
|
2078
|
-
this.lastInfinityTotal = total;
|
|
2079
|
-
this.lastInfinityPageRequested = null;
|
|
2080
|
-
}
|
|
2081
|
-
});
|
|
2082
|
-
effect(() => {
|
|
2083
|
-
const selection = this.selection();
|
|
2084
|
-
if ('defaultSelected' in selection) {
|
|
2085
|
-
this.selector.selectedKeys.set(selection.defaultSelected || []);
|
|
2086
|
-
}
|
|
2087
|
-
});
|
|
2088
|
-
effect(() => {
|
|
2089
|
-
const selection = this.selection();
|
|
2090
|
-
if (selection.mode === 'none' || !('key' in selection)) {
|
|
2091
|
-
return;
|
|
2092
|
-
}
|
|
2093
|
-
const data = this.data() ?? [];
|
|
2094
|
-
const key = selection.key;
|
|
2095
|
-
const keySet = new Set(data.map((row) => row[key]));
|
|
2096
|
-
this.selector.selectedKeys.update((prev) => prev.filter((k) => keySet.has(k)));
|
|
2097
|
-
});
|
|
2098
|
-
effect(() => {
|
|
2099
|
-
const data = this.data() ?? [];
|
|
2100
|
-
const predicate = this.isRowSticky();
|
|
2101
|
-
if (!predicate || !data.length) {
|
|
2102
|
-
this.stickyIndexes.set([]);
|
|
2103
|
-
this.updateStickyFromScroll();
|
|
2104
|
-
return;
|
|
2105
|
-
}
|
|
2106
|
-
const indexes = [];
|
|
2107
|
-
for (let i = 0; i < data.length; i++) {
|
|
2108
|
-
if (predicate(data[i], i)) {
|
|
2109
|
-
indexes.push(i);
|
|
2110
|
-
}
|
|
2111
|
-
}
|
|
2112
|
-
this.stickyIndexes.set(indexes);
|
|
2113
|
-
this.updateStickyFromScroll();
|
|
2114
|
-
});
|
|
2115
|
-
inject(DestroyRef).onDestroy(() => {
|
|
2116
|
-
this.subscription.unsubscribe();
|
|
2117
|
-
this.observer?.disconnect();
|
|
2118
|
-
clearTimeout(this.hideSbTimeout);
|
|
2119
|
-
this.clearScrollRaf();
|
|
2120
|
-
this.clearScrollbarRaf();
|
|
2121
|
-
this.clearStickyRaf();
|
|
2122
|
-
});
|
|
2123
|
-
}
|
|
2124
|
-
resolvePinnedData(pr) {
|
|
2125
|
-
return typeof pr.data === 'function' ? pr.data() : pr.data;
|
|
2126
|
-
}
|
|
2127
|
-
/**
|
|
2128
|
-
* Handles column header click for sorting.
|
|
2129
|
-
*
|
|
2130
|
-
* Toggles sort order between ascending and descending for the clicked column.
|
|
2131
|
-
* If a different column is clicked, it resets to ascending order.
|
|
2132
|
-
* Emits sortChange event with the current sort state.
|
|
2133
|
-
*
|
|
2134
|
-
* @param col - Column configuration that was clicked
|
|
2135
|
-
*/
|
|
2136
|
-
onSort(col) {
|
|
2137
|
-
if (!col.sortKey) {
|
|
2138
|
-
return;
|
|
2139
|
-
}
|
|
2140
|
-
if (this.currentSortField === col.sortKey) {
|
|
2141
|
-
this.currentSortOrder = this.currentSortOrder === 'asc' ? 'desc' : 'asc';
|
|
2142
|
-
}
|
|
2143
|
-
else {
|
|
2144
|
-
this.currentSortField = col.sortKey;
|
|
2145
|
-
this.currentSortOrder = 'asc';
|
|
2146
|
-
}
|
|
2147
|
-
this.sortChange.emit({ key: this.currentSortField, order: this.currentSortOrder });
|
|
2148
|
-
}
|
|
2149
|
-
/**
|
|
2150
|
-
* Handles cell click events.
|
|
2151
|
-
*
|
|
2152
|
-
* Emits cellClick event and handles row selection if selection mode is enabled.
|
|
2153
|
-
* Updates the selection state and emits selectChange event accordingly.
|
|
2154
|
-
*
|
|
2155
|
-
* @param row - Data row that was clicked
|
|
2156
|
-
* @param col - Column configuration of the clicked cell
|
|
2157
|
-
* @param index - Row index in the dataset
|
|
2158
|
-
* @param event
|
|
2159
|
-
*/
|
|
2160
|
-
onCellClick(row, col, index, event) {
|
|
2161
|
-
this.cellClick.emit({ row, col, index, event });
|
|
2162
|
-
if (this.selection().mode !== 'none') {
|
|
2163
|
-
const selected = this.selector.select(row);
|
|
2164
|
-
this.selectChange.emit({ selected });
|
|
2165
|
-
}
|
|
2166
|
-
}
|
|
2167
|
-
onCellContext(row, col, index, event) {
|
|
2168
|
-
this.cellContext.emit({ row, col, index, event });
|
|
2169
|
-
}
|
|
2170
|
-
onCellDoubleClick(row, col, index, event) {
|
|
2171
|
-
this.cellDoubleClick.emit({ row, col, index, event });
|
|
2172
|
-
}
|
|
2173
|
-
isExpandable(column) {
|
|
2174
|
-
const keys = this.expanderMap();
|
|
2175
|
-
return keys.has(column.key);
|
|
2176
|
-
}
|
|
2177
|
-
/**
|
|
2178
|
-
* Handles column expand/collapse toggle.
|
|
2179
|
-
*
|
|
2180
|
-
* Toggles the expanded state of a column and updates the visibility
|
|
2181
|
-
* of all dependent columns that reference this column via expandBy property.
|
|
2182
|
-
*
|
|
2183
|
-
* @param column - Column configuration to expand or collapse
|
|
2184
|
-
*/
|
|
2185
|
-
onExpand(column) {
|
|
2186
|
-
const expanded = !this.expanderMap().get(column.key);
|
|
2187
|
-
this.expanderMap.update((prev) => {
|
|
2188
|
-
const map = new Map(prev);
|
|
2189
|
-
map.set(column.key, expanded);
|
|
2190
|
-
return map;
|
|
2191
|
-
});
|
|
2192
|
-
const columns = this.extendedColumns().map((col) => col.expandBy === column.key ? { ...col, visible: expanded } : col);
|
|
2193
|
-
this.vm.columns.set(columns);
|
|
2194
|
-
}
|
|
2195
|
-
/**
|
|
2196
|
-
* Handles vertical scroll events and updates visible rows.
|
|
2197
|
-
*
|
|
2198
|
-
* Implements virtual scrolling by calculating which rows should be rendered
|
|
2199
|
-
* based on the current scroll position. Also handles infinite scroll data loading
|
|
2200
|
-
* when the user scrolls near the end of available data.
|
|
2201
|
-
*
|
|
2202
|
-
* @param initial - Whether this is the initial scroll calculation
|
|
2203
|
-
*/
|
|
2204
|
-
onVerticalScroll(initial = false) {
|
|
2205
|
-
const el = this.scrollEl()?.nativeElement;
|
|
2206
|
-
if (!el)
|
|
2207
|
-
return;
|
|
2208
|
-
const scrollTop = el.scrollTop ?? 0;
|
|
2209
|
-
const viewportHeight = el.clientHeight ?? 0;
|
|
2210
|
-
const data = this.data() ?? [];
|
|
2211
|
-
const total = data.length ?? 0;
|
|
2212
|
-
const rowHeight = Math.max(1, this.rowHeight());
|
|
2213
|
-
const virtualBuffer = Math.max(0, this.virtualBuffer());
|
|
2214
|
-
const pinnedTopH = this.vm.pinnedTop().length * rowHeight;
|
|
2215
|
-
const limit = this.mode() === 'pagination';
|
|
2216
|
-
const cap = limit ? Math.max(1, this.pageSize()) : Number.POSITIVE_INFINITY;
|
|
2217
|
-
const viewportRows = Math.ceil(viewportHeight / rowHeight);
|
|
2218
|
-
const visibleCount = Math.min(viewportRows + virtualBuffer, cap);
|
|
2219
|
-
const start = Math.max(0, Math.floor(scrollTop / rowHeight) - Math.floor(virtualBuffer / 2));
|
|
2220
|
-
const end = Math.min(total, start + visibleCount);
|
|
2221
|
-
if (viewportHeight <= 0 || !isFinite(viewportHeight) || !isFinite(rowHeight)) {
|
|
2222
|
-
const fallbackCount = Math.min(total, Math.max(1, this.pageSize()));
|
|
2223
|
-
this.renderCount = fallbackCount;
|
|
2224
|
-
this.ensureRenderSlots(fallbackCount);
|
|
2225
|
-
this.updateStickyRow(scrollTop, rowHeight, pinnedTopH);
|
|
2226
|
-
this.finalizeScroll();
|
|
2227
|
-
return;
|
|
2228
|
-
}
|
|
2229
|
-
if (start === this.lastStartIndex && end === this.lastEndIndex) {
|
|
2230
|
-
this.finalizeScroll();
|
|
2231
|
-
return;
|
|
2232
|
-
}
|
|
2233
|
-
this.lastStartIndex = start;
|
|
2234
|
-
this.lastEndIndex = end;
|
|
2235
|
-
this.startIndex = start;
|
|
2236
|
-
this.renderCount = Math.max(0, end - start);
|
|
2237
|
-
this.ensureRenderSlots(this.renderCount);
|
|
2238
|
-
this.updateStickyRow(scrollTop, rowHeight, pinnedTopH);
|
|
2239
|
-
if (!initial && this.mode() === 'infinity') {
|
|
2240
|
-
const threshold = Math.max(0, total - Math.max(1, Math.floor(visibleCount * 1.5)));
|
|
2241
|
-
const nearEnd = start + visibleCount >= threshold;
|
|
2242
|
-
if (nearEnd && !this.loading()) {
|
|
2243
|
-
const modifier = this.pageStartFromZero() ? 0 : 1;
|
|
2244
|
-
const page = Math.floor(total / this.pageSize()) + modifier;
|
|
2245
|
-
if (this.lastInfinityPageRequested === page && this.lastInfinityTotal === total) {
|
|
2246
|
-
return;
|
|
2247
|
-
}
|
|
2248
|
-
this.lastInfinityTotal = total;
|
|
2249
|
-
this.lastInfinityPageRequested = page;
|
|
2250
|
-
this.pageChange.emit({ page, rows: this.pageSize() });
|
|
2251
|
-
}
|
|
2252
|
-
}
|
|
2253
|
-
this.finalizeScroll();
|
|
2254
|
-
}
|
|
2255
|
-
onHorizontalScroll() { }
|
|
2256
|
-
/**
|
|
2257
|
-
* Handles mouse down event on scrollbar thumb for drag scrolling.
|
|
2258
|
-
*
|
|
2259
|
-
* Initiates dragging mode and sets up mouse move/up event listeners
|
|
2260
|
-
* to track thumb position and update scroll position accordingly.
|
|
2261
|
-
* Automatically cleans up listeners when dragging ends.
|
|
2262
|
-
*
|
|
2263
|
-
* @param e - Mouse down event from a scrollbar thumb element
|
|
2264
|
-
*/
|
|
2265
|
-
onThumbDown(e) {
|
|
2266
|
-
e.preventDefault();
|
|
2267
|
-
e.stopPropagation();
|
|
2268
|
-
const el = this.scrollEl()?.nativeElement;
|
|
2269
|
-
if (!el)
|
|
2270
|
-
return;
|
|
2271
|
-
this.vm.dragging = true;
|
|
2272
|
-
this.showScrollbar();
|
|
2273
|
-
const startY = e.clientY;
|
|
2274
|
-
const startTop = this.vm.thumbTopPx();
|
|
2275
|
-
const state = computeScrollbarState(el.scrollHeight || 1, el.clientHeight || 1, el.scrollTop || 0);
|
|
2276
|
-
const maxThumbTop = state.maxThumbTop || 1;
|
|
2277
|
-
const maxScrollTop = state.maxScrollTop || 1;
|
|
2278
|
-
const onMove = (ev) => {
|
|
2279
|
-
const delta = ev.clientY - startY;
|
|
2280
|
-
const newTop = clampThumbTop(startTop + delta, maxThumbTop);
|
|
2281
|
-
this.vm.thumbTopPx.set(newTop);
|
|
2282
|
-
el.scrollTop = mapThumbTopToScrollTop(newTop, maxThumbTop, maxScrollTop);
|
|
2283
|
-
this.vm.calcScrollbar();
|
|
2284
|
-
this.showScrollbar();
|
|
2285
|
-
};
|
|
2286
|
-
const onUp = () => {
|
|
2287
|
-
window.removeEventListener('mousemove', onMove);
|
|
2288
|
-
window.removeEventListener('mouseup', onUp);
|
|
2289
|
-
this.vm.dragging = false;
|
|
2290
|
-
this.hideScrollbarSoon();
|
|
2291
|
-
};
|
|
2292
|
-
window.addEventListener('mousemove', onMove);
|
|
2293
|
-
window.addEventListener('mouseup', onUp);
|
|
2294
|
-
}
|
|
2295
|
-
showScrollbar() {
|
|
2296
|
-
this.vm.scrollbarVisible.set(true);
|
|
2297
|
-
clearTimeout(this.hideSbTimeout);
|
|
2298
|
-
}
|
|
2299
|
-
hideScrollbarSoon(delay = 1200) {
|
|
2300
|
-
if (this.vm.dragging) {
|
|
2301
|
-
return;
|
|
2302
|
-
}
|
|
2303
|
-
clearTimeout(this.hideSbTimeout);
|
|
2304
|
-
this.hideSbTimeout = setTimeout(() => this.vm.scrollbarVisible.set(false), delay);
|
|
2305
|
-
}
|
|
2306
|
-
trackPinnedRow = (row) => row.order;
|
|
2307
|
-
cellClass(col, row) {
|
|
2308
|
-
if (typeof col.cellClass === 'function') {
|
|
2309
|
-
return col.cellClass(row);
|
|
2310
|
-
}
|
|
2311
|
-
return col.cellClass;
|
|
2312
|
-
}
|
|
2313
|
-
ariaSort(col) {
|
|
2314
|
-
if (this.currentSortField !== col.sortKey) {
|
|
2315
|
-
return 'none';
|
|
2316
|
-
}
|
|
2317
|
-
return this.currentSortOrder === 'asc' ? 'ascending' : 'descending';
|
|
2318
|
-
}
|
|
2319
|
-
resolveTooltip(row, col) {
|
|
2320
|
-
const tooltip = col.tooltip;
|
|
2321
|
-
if (!tooltip) {
|
|
2322
|
-
return null;
|
|
2323
|
-
}
|
|
2324
|
-
if (typeof tooltip === 'function') {
|
|
2325
|
-
const value = tooltip(row);
|
|
2326
|
-
if (value === null || value === undefined || value === '') {
|
|
2327
|
-
return null;
|
|
2328
|
-
}
|
|
2329
|
-
return String(value);
|
|
2330
|
-
}
|
|
2331
|
-
if (typeof tooltip === 'string') {
|
|
2332
|
-
return tooltip === '' ? null : tooltip;
|
|
2333
|
-
}
|
|
2334
|
-
return tooltip;
|
|
2335
|
-
}
|
|
2336
|
-
showTooltip(event, row, col, index) {
|
|
2337
|
-
const resolved = this.resolveTooltip(row, col);
|
|
2338
|
-
if (!resolved) {
|
|
2339
|
-
return;
|
|
2340
|
-
}
|
|
2341
|
-
const baseValue = 'value' in col ? col.value(row) : row[col.key];
|
|
2342
|
-
const ctx = {
|
|
2343
|
-
$implicit: row,
|
|
2344
|
-
row,
|
|
2345
|
-
col: col,
|
|
2346
|
-
index,
|
|
2347
|
-
value: baseValue,
|
|
2348
|
-
};
|
|
2349
|
-
const offset = 12;
|
|
2350
|
-
const x = event.clientX + offset;
|
|
2351
|
-
const y = event.clientY + offset;
|
|
2352
|
-
if (typeof resolved === 'string') {
|
|
2353
|
-
this.tooltipState.set({ text: resolved, x, y, visible: true });
|
|
2354
|
-
}
|
|
2355
|
-
else {
|
|
2356
|
-
this.tooltipState.set({ tpl: resolved, ctx, x, y, visible: true });
|
|
2357
|
-
}
|
|
2358
|
-
requestAnimationFrame(() => this.positionTooltip());
|
|
2359
|
-
}
|
|
2360
|
-
hideTooltip() {
|
|
2361
|
-
if (!this.tooltipState().visible) {
|
|
2362
|
-
return;
|
|
2363
|
-
}
|
|
2364
|
-
this.tooltipState.update((prev) => ({ ...prev, visible: false }));
|
|
2365
|
-
}
|
|
2366
|
-
isStickyRowIndex(index) {
|
|
2367
|
-
return this.stickyRowIndex() === index;
|
|
2368
|
-
}
|
|
2369
|
-
resolveRowTemplate(row, index) {
|
|
2370
|
-
const resolver = this.getRowTemplate();
|
|
2371
|
-
if (resolver) {
|
|
2372
|
-
const resolved = resolver(row, index);
|
|
2373
|
-
if (resolved) {
|
|
2374
|
-
return resolved;
|
|
2375
|
-
}
|
|
2376
|
-
}
|
|
2377
|
-
return this.rowTpl()?.tpl ?? null;
|
|
2378
|
-
}
|
|
2379
|
-
initVm() {
|
|
2380
|
-
this.vm.scrollEl.set(this.scrollEl());
|
|
2381
|
-
this.vm.columns.set(this.extendedColumns());
|
|
2382
|
-
this.vm.pinnedRows.set(this.pinnedRows());
|
|
2383
|
-
this.vm.headerGroups.set(this.headerGroups());
|
|
2384
|
-
queueMicrotask(() => this.initHeader());
|
|
2385
|
-
}
|
|
2386
|
-
initSelector() {
|
|
2387
|
-
this.selector.data.set(this.data());
|
|
2388
|
-
this.selector.selection.set(this.selection());
|
|
2389
|
-
}
|
|
2390
|
-
initRefs() {
|
|
2391
|
-
this.vm.globalTypeCellTpls.clear();
|
|
2392
|
-
this.vm.globalDataCellTpls.clear();
|
|
2393
|
-
this.vm.globalRowCellTpls.clear();
|
|
2394
|
-
this.cellTypedSlotRefs()?.forEach((dir) => this.vm.globalTypeCellTpls.set(dir.type(), dir.tpl));
|
|
2395
|
-
this.cellDataSlotRefs()?.forEach((dir) => this.vm.globalDataCellTpls.set(dir.key(), dir.tpl));
|
|
2396
|
-
this.declarativeColumns().rowCellTemplatesByKey.forEach((tpl, key) => this.vm.globalRowCellTpls.set(key, tpl));
|
|
2397
|
-
const headerMap = new Map();
|
|
2398
|
-
this.headerSlotRefs()?.forEach((h) => headerMap.set(h.key(), h.tpl));
|
|
2399
|
-
this.extendedColumns()?.forEach((c) => {
|
|
2400
|
-
if (!c.headerTemplate && headerMap.has(c.key)) {
|
|
2401
|
-
c.headerTemplate = headerMap.get(c.key);
|
|
2402
|
-
}
|
|
2403
|
-
});
|
|
2404
|
-
}
|
|
2405
|
-
initSort() {
|
|
2406
|
-
const firstSortable = this.extendedColumns()?.find((c) => !!c.sortKey);
|
|
2407
|
-
if (firstSortable) {
|
|
2408
|
-
this.currentSortField = firstSortable.sortKey;
|
|
2409
|
-
this.currentSortOrder = 'asc';
|
|
2410
|
-
}
|
|
2411
|
-
}
|
|
2412
|
-
initHeader() {
|
|
2413
|
-
const el = this.headerEl()?.nativeElement;
|
|
2414
|
-
if (!el)
|
|
2415
|
-
return;
|
|
2416
|
-
this.headerHeight.set(el.offsetHeight);
|
|
2417
|
-
}
|
|
2418
|
-
initScroll() {
|
|
2419
|
-
const scrollEl = this.scrollEl()?.nativeElement;
|
|
2420
|
-
if (scrollEl) {
|
|
2421
|
-
const scrollDetector = new ScrollDirectionDetector(scrollEl);
|
|
2422
|
-
this.ngZone.runOutsideAngular(() => {
|
|
2423
|
-
this.subscription.add(fromEvent(scrollEl, 'scroll').subscribe(() => this.scheduleScrollTick(scrollDetector)));
|
|
2424
|
-
this.subscription.add(fromEvent(scrollEl, 'scroll').subscribe(() => this.scheduleStickyUpdate()));
|
|
2425
|
-
});
|
|
2426
|
-
}
|
|
2427
|
-
queueMicrotask(() => this.onVerticalScroll(true));
|
|
2428
|
-
}
|
|
2429
|
-
initObserver() {
|
|
2430
|
-
const root = this.rootEl();
|
|
2431
|
-
if (!root)
|
|
2432
|
-
return;
|
|
2433
|
-
this.subscription.add(this.resizeSubject.pipe(debounceTime(this.defaults.debounce.resize)).subscribe((entries) => {
|
|
2434
|
-
const rect = entries[0].contentRect;
|
|
2435
|
-
const width = rect.width;
|
|
2436
|
-
const height = rect.height;
|
|
2437
|
-
const widthChanged = width !== this.lastResizeWidth;
|
|
2438
|
-
const heightChanged = height !== this.lastResizeHeight;
|
|
2439
|
-
if (!widthChanged && !heightChanged) {
|
|
2440
|
-
return;
|
|
2441
|
-
}
|
|
2442
|
-
this.lastResizeWidth = width;
|
|
2443
|
-
this.lastResizeHeight = height;
|
|
2444
|
-
if (widthChanged) {
|
|
2445
|
-
this.vm.containerWidth.set(width);
|
|
2446
|
-
queueMicrotask(() => this.initHeader());
|
|
2447
|
-
}
|
|
2448
|
-
if (heightChanged) {
|
|
2449
|
-
this.onVerticalScroll(true);
|
|
2450
|
-
}
|
|
2451
|
-
}));
|
|
2452
|
-
this.observer = new ResizeObserver((entries) => this.resizeSubject.next(entries));
|
|
2453
|
-
this.observer.observe(root.nativeElement);
|
|
2454
|
-
}
|
|
2455
|
-
initExpander() {
|
|
2456
|
-
const map = new Map();
|
|
2457
|
-
const columns = untracked(() => this.vm.columns().map((col) => {
|
|
2458
|
-
col.expandBy && map.set(col.expandBy, false);
|
|
2459
|
-
return { ...col, visible: col.expandBy ? false : col.visible };
|
|
2460
|
-
}));
|
|
2461
|
-
this.vm.columns.set(columns);
|
|
2462
|
-
this.expanderMap.set(map);
|
|
2463
|
-
}
|
|
2464
|
-
positionTooltip() {
|
|
2465
|
-
const el = this.tooltipEl()?.nativeElement;
|
|
2466
|
-
const state = this.tooltipState();
|
|
2467
|
-
if (!el || !state.visible) {
|
|
2468
|
-
return;
|
|
2469
|
-
}
|
|
2470
|
-
const rect = el.getBoundingClientRect();
|
|
2471
|
-
const padding = 8;
|
|
2472
|
-
const vw = window.innerWidth;
|
|
2473
|
-
const vh = window.innerHeight;
|
|
2474
|
-
let x = state.x;
|
|
2475
|
-
let y = state.y;
|
|
2476
|
-
if (x + rect.width + padding > vw) {
|
|
2477
|
-
x = Math.max(padding, vw - rect.width - padding);
|
|
2478
|
-
}
|
|
2479
|
-
if (y + rect.height + padding > vh) {
|
|
2480
|
-
y = Math.max(padding, vh - rect.height - padding);
|
|
2481
|
-
}
|
|
2482
|
-
if (x !== state.x || y !== state.y) {
|
|
2483
|
-
this.tooltipState.update((prev) => ({ ...prev, x, y }));
|
|
2484
|
-
}
|
|
2485
|
-
}
|
|
2486
|
-
scheduleScrollbarUpdate() {
|
|
2487
|
-
if (this.scrollbarRafId !== null) {
|
|
2488
|
-
return;
|
|
2489
|
-
}
|
|
2490
|
-
this.scrollbarRafId = requestAnimationFrame(() => {
|
|
2491
|
-
this.scrollbarRafId = null;
|
|
2492
|
-
this.vm.calcScrollbar();
|
|
2493
|
-
});
|
|
2494
|
-
}
|
|
2495
|
-
finalizeScroll() {
|
|
2496
|
-
this.scheduleScrollbarUpdate();
|
|
2497
|
-
this.showScrollbar();
|
|
2498
|
-
this.hideScrollbarSoon();
|
|
2499
|
-
}
|
|
2500
|
-
scheduleScrollTick(detector) {
|
|
2501
|
-
if (this.scrollRafId !== null) {
|
|
2502
|
-
return;
|
|
2503
|
-
}
|
|
2504
|
-
this.scrollRafId = requestAnimationFrame(() => {
|
|
2505
|
-
this.scrollRafId = null;
|
|
2506
|
-
const direction = detector.detect();
|
|
2507
|
-
this.ngZone.run(() => {
|
|
2508
|
-
direction === 'vertical' ? this.onVerticalScroll() : this.onHorizontalScroll();
|
|
2509
|
-
this.hideTooltip();
|
|
2510
|
-
});
|
|
2511
|
-
});
|
|
2512
|
-
}
|
|
2513
|
-
clearScrollRaf() {
|
|
2514
|
-
if (this.scrollRafId !== null) {
|
|
2515
|
-
cancelAnimationFrame(this.scrollRafId);
|
|
2516
|
-
this.scrollRafId = null;
|
|
2517
|
-
}
|
|
2518
|
-
}
|
|
2519
|
-
ensureRenderSlots(count) {
|
|
2520
|
-
const current = this.renderSlots();
|
|
2521
|
-
if (current.length === count) {
|
|
2522
|
-
return;
|
|
2523
|
-
}
|
|
2524
|
-
const next = new Array(count);
|
|
2525
|
-
for (let i = 0; i < count; i++) {
|
|
2526
|
-
next[i] = i;
|
|
2527
|
-
}
|
|
2528
|
-
this.renderSlots.set(next);
|
|
2529
|
-
}
|
|
2530
|
-
clearScrollbarRaf() {
|
|
2531
|
-
if (this.scrollbarRafId !== null) {
|
|
2532
|
-
cancelAnimationFrame(this.scrollbarRafId);
|
|
2533
|
-
this.scrollbarRafId = null;
|
|
2534
|
-
}
|
|
2535
|
-
}
|
|
2536
|
-
scheduleStickyUpdate() {
|
|
2537
|
-
if (this.stickyRafId !== null) {
|
|
2538
|
-
return;
|
|
2539
|
-
}
|
|
2540
|
-
this.stickyRafId = requestAnimationFrame(() => {
|
|
2541
|
-
this.stickyRafId = null;
|
|
2542
|
-
this.ngZone.run(() => this.updateStickyFromScroll());
|
|
2543
|
-
});
|
|
2544
|
-
}
|
|
2545
|
-
clearStickyRaf() {
|
|
2546
|
-
if (this.stickyRafId !== null) {
|
|
2547
|
-
cancelAnimationFrame(this.stickyRafId);
|
|
2548
|
-
this.stickyRafId = null;
|
|
2549
|
-
}
|
|
2550
|
-
}
|
|
2551
|
-
updateStickyRow(scrollTop, rowHeight, pinnedTopH) {
|
|
2552
|
-
const indexes = this.stickyIndexes();
|
|
2553
|
-
if (!indexes.length) {
|
|
2554
|
-
this.stickyRowIndex.set(null);
|
|
2555
|
-
return;
|
|
2556
|
-
}
|
|
2557
|
-
const scrollRowIndex = Math.max(0, Math.floor(scrollTop / rowHeight));
|
|
2558
|
-
const currentIndex = this.findStickyIndexBefore(indexes, scrollRowIndex);
|
|
2559
|
-
if (currentIndex === null) {
|
|
2560
|
-
this.stickyRowIndex.set(null);
|
|
2561
|
-
return;
|
|
2562
|
-
}
|
|
2563
|
-
let activeIndex = currentIndex;
|
|
2564
|
-
const prevIndex = this.stickyRowIndex();
|
|
2565
|
-
const hysteresisPx = Math.max(1, rowHeight * 0.5);
|
|
2566
|
-
if (prevIndex !== null && prevIndex !== currentIndex) {
|
|
2567
|
-
const boundaryPx = currentIndex * rowHeight;
|
|
2568
|
-
const delta = Math.abs(scrollTop - boundaryPx);
|
|
2569
|
-
if (delta < hysteresisPx) {
|
|
2570
|
-
activeIndex = prevIndex;
|
|
2571
|
-
}
|
|
2572
|
-
}
|
|
2573
|
-
const stickyTop = this.headerHeight() + pinnedTopH;
|
|
2574
|
-
const topPx = scrollTop + stickyTop;
|
|
2575
|
-
this.stickyRowIndex.set(activeIndex);
|
|
2576
|
-
this.stickyRowTopPx.set(topPx);
|
|
2577
|
-
}
|
|
2578
|
-
updateStickyFromScroll() {
|
|
2579
|
-
const el = this.scrollEl()?.nativeElement;
|
|
2580
|
-
if (!el)
|
|
2581
|
-
return;
|
|
2582
|
-
const scrollTop = el.scrollTop ?? 0;
|
|
2583
|
-
const rowHeight = Math.max(1, this.rowHeight());
|
|
2584
|
-
const pinnedTopH = this.vm.pinnedTop().length * rowHeight;
|
|
2585
|
-
this.updateStickyRow(scrollTop, rowHeight, pinnedTopH);
|
|
2586
|
-
}
|
|
2587
|
-
findStickyIndexBefore(indexes, index) {
|
|
2588
|
-
let low = 0;
|
|
2589
|
-
let high = indexes.length - 1;
|
|
2590
|
-
let result = -1;
|
|
2591
|
-
while (low <= high) {
|
|
2592
|
-
const mid = (low + high) >>> 1;
|
|
2593
|
-
const value = indexes[mid];
|
|
2594
|
-
if (value <= index) {
|
|
2595
|
-
result = value;
|
|
2596
|
-
low = mid + 1;
|
|
2597
|
-
}
|
|
2598
|
-
else {
|
|
2599
|
-
high = mid - 1;
|
|
2600
|
-
}
|
|
2601
|
-
}
|
|
2602
|
-
return result === -1 ? null : result;
|
|
2603
|
-
}
|
|
2604
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.1", ngImport: i0, type: DataGrid, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
2605
|
-
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 }, lockVerticalScroll: { classPropertyName: "lockVerticalScroll", publicName: "lockVerticalScroll", 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 }, deferContent: { classPropertyName: "deferContent", publicName: "deferContent", isSignal: true, isRequired: false, transformFunction: null }, deferHeader: { classPropertyName: "deferHeader", publicName: "deferHeader", isSignal: true, isRequired: false, transformFunction: null }, deferPinned: { classPropertyName: "deferPinned", publicName: "deferPinned", isSignal: true, isRequired: false, transformFunction: null }, deferCells: { classPropertyName: "deferCells", publicName: "deferCells", isSignal: true, isRequired: false, transformFunction: null }, deferIcons: { classPropertyName: "deferIcons", publicName: "deferIcons", isSignal: true, isRequired: false, transformFunction: null }, deferTooltip: { classPropertyName: "deferTooltip", publicName: "deferTooltip", 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 [class.lock-vertical-scroll]=\"lockVerticalScroll()\"\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 <ng-template #headerContent>\r\n <div\r\n class=\"re-dg-header\"\r\n role=\"rowgroup\"\r\n [style.width.px]=\"vm.contentWidth()\"\r\n [style.min-width.%]=\"100\"\r\n >\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 @if (deferIcons()) {\r\n @defer (when true) {\r\n <re-checkbox-ic [state]=\"selector.isAllSelected()\" (click)=\"selector.selectAll()\" />\r\n } @placeholder {\r\n <span class=\"re-dg-icon-placeholder\"></span>\r\n } @loading {\r\n <span class=\"re-dg-icon-placeholder\"></span>\r\n }\r\n } @else {\r\n <re-checkbox-ic [state]=\"selector.isAllSelected()\" (click)=\"selector.selectAll()\" />\r\n }\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 @if (deferIcons()) {\r\n @defer (when true) {\r\n <re-sort-ic [direction]=\"direction\" />\r\n } @placeholder {\r\n <span class=\"re-dg-icon-placeholder\"></span>\r\n } @loading {\r\n <span class=\"re-dg-icon-placeholder\"></span>\r\n }\r\n } @else {\r\n <re-sort-ic [direction]=\"direction\" />\r\n }\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 @if (deferIcons()) {\r\n @defer (when true) {\r\n <re-expand-ic [expanded]=\"expanded\" />\r\n } @placeholder {\r\n <span class=\"re-dg-icon-placeholder\"></span>\r\n } @loading {\r\n <span class=\"re-dg-icon-placeholder\"></span>\r\n }\r\n } @else {\r\n <re-expand-ic [expanded]=\"expanded\" />\r\n }\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 <ng-template #pinnedTopContent>\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 <ng-container\r\n [ngTemplateOutlet]=\"pinnedRowCells\"\r\n [ngTemplateOutletContext]=\"{ $implicit: resolvePinnedData(pr) }\"\r\n />\r\n }\r\n </div>\r\n }\r\n </ng-template>\r\n\r\n @if (deferPinned()) {\r\n @defer (when true) {\r\n <ng-container [ngTemplateOutlet]=\"pinnedTopContent\" />\r\n } @placeholder {\r\n <div class=\"re-dg-row re-dg-pinned re-dg-top re-dg-deferred-placeholder\" [style.min-height.px]=\"rowH\"></div>\r\n } @loading {\r\n <div class=\"re-dg-row re-dg-pinned re-dg-top re-dg-deferred-placeholder\" [style.min-height.px]=\"rowH\"></div>\r\n }\r\n } @else {\r\n <ng-container [ngTemplateOutlet]=\"pinnedTopContent\" />\r\n }\r\n }\r\n </div>\r\n </ng-template>\r\n\r\n @if (deferHeader()) {\r\n @defer (when true) {\r\n <ng-container [ngTemplateOutlet]=\"headerContent\" />\r\n } @placeholder {\r\n <div class=\"re-dg-header re-dg-deferred-placeholder\" [style.min-height.px]=\"headerHeight()\"></div>\r\n } @loading {\r\n <div class=\"re-dg-header re-dg-deferred-placeholder\" [style.min-height.px]=\"headerHeight()\"></div>\r\n }\r\n } @else {\r\n <ng-container [ngTemplateOutlet]=\"headerContent\" />\r\n }\r\n\r\n <ng-template #dataCellContent let-row let-col=\"col\" let-index=\"index\">\r\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 @if (deferCells()) {\r\n @defer (when true) {\r\n <re-data-grid-cell [index]=\"index\" [item]=\"row\" [column]=\"col\" />\r\n } @placeholder {\r\n <span class=\"re-dg-cell-deferred\"></span>\r\n } @loading {\r\n <span class=\"re-dg-cell-deferred\"></span>\r\n }\r\n } @else {\r\n <re-data-grid-cell [index]=\"index\" [item]=\"row\" [column]=\"col\" />\r\n }\r\n }\r\n </ng-template>\r\n\r\n <ng-template #gridContent>\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\r\n class=\"re-dg-cell\"\r\n role=\"cell\"\r\n [class.expanded]=\"!!col.expandBy\"\r\n [class]=\"cellClass(col, stickyRow)\"\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.justify-items]=\"col.align || 'left'\"\r\n [style.text-align]=\"col.align || 'left'\"\r\n [style.width.px]=\"vm.widthByKey(col.key)\"\r\n [attr.tabindex]=\"0\"\r\n (mouseenter)=\"showTooltip($event, stickyRow, col, stickyIndex)\"\r\n (mouseleave)=\"hideTooltip()\"\r\n (click)=\"onCellClick(stickyRow, col, stickyIndex, $event);\"\r\n (contextmenu)=\"onCellContext(stickyRow, col, stickyIndex, $event)\"\r\n (dblclick)=\"onCellDoubleClick(stickyRow, col, stickyIndex, $event)\"\r\n (keydown.enter)=\"onCellClick(stickyRow, col, stickyIndex, $event)\"\r\n >\r\n <ng-container\r\n [ngTemplateOutlet]=\"dataCellContent\"\r\n [ngTemplateOutletContext]=\"{ $implicit: stickyRow, col: col, index: stickyIndex }\"\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\r\n <div\r\n class=\"re-dg-cell\"\r\n role=\"cell\"\r\n [class.expanded]=\"!!col.expandBy\"\r\n [class]=\"cellClass(col, row)\"\r\n [class.sticky-left]=\"isLeft\"\r\n [class.sticky-right]=\"isRight\"\r\n [style.left.px]=\"stickyLeft\"\r\n [style.right.px]=\"stickyRight\"\r\n [style.justify-items]=\"col.align || 'left'\"\r\n [style.text-align]=\"col.align || 'left'\"\r\n [style.width.px]=\"vm.widthByKey(col.key)\"\r\n [attr.tabindex]=\"0\"\r\n (mouseenter)=\"showTooltip($event, row, col, rowIndex)\"\r\n (mouseleave)=\"hideTooltip()\"\r\n (click)=\"onCellClick(row, col, rowIndex, $event);\"\r\n (contextmenu)=\"onCellContext(row, col, rowIndex, $event)\"\r\n (dblclick)=\"onCellDoubleClick(row, col, rowIndex, $event)\"\r\n (keydown.enter)=\"onCellClick(row, col, rowIndex, $event)\"\r\n >\r\n <ng-container\r\n [ngTemplateOutlet]=\"dataCellContent\"\r\n [ngTemplateOutletContext]=\"{ $implicit: row, col: col, index: rowIndex }\"\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 <ng-template #pinnedBottomContent>\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 <ng-container\r\n [ngTemplateOutlet]=\"pinnedRowCells\"\r\n [ngTemplateOutletContext]=\"{ $implicit: resolvePinnedData(pr) }\"\r\n />\r\n }\r\n </div>\r\n }\r\n </div>\r\n </ng-template>\r\n\r\n @if (deferPinned()) {\r\n @defer (when true) {\r\n <ng-container [ngTemplateOutlet]=\"pinnedBottomContent\" />\r\n } @placeholder {\r\n <div class=\"re-dg-footer re-dg-deferred-placeholder\" [style.min-height.px]=\"rowH\"></div>\r\n } @loading {\r\n <div class=\"re-dg-footer re-dg-deferred-placeholder\" [style.min-height.px]=\"rowH\"></div>\r\n }\r\n } @else {\r\n <ng-container [ngTemplateOutlet]=\"pinnedBottomContent\" />\r\n }\r\n }\r\n </ng-template>\r\n\r\n @if (deferContent()) {\r\n @defer (when true) {\r\n <ng-container [ngTemplateOutlet]=\"gridContent\" />\r\n } @placeholder {\r\n <div class=\"re-dg-deferred-placeholder\" [style.min-height.px]=\"rowH * 3\"></div>\r\n } @loading {\r\n <div class=\"re-dg-deferred-placeholder\" [style.min-height.px]=\"rowH * 3\"></div>\r\n }\r\n } @else {\r\n <ng-container [ngTemplateOutlet]=\"gridContent\" />\r\n }\r\n </div>\r\n\r\n <ng-template #pinnedRowCells let-row>\n @for (col of cols; track col.key) {\n @let isCheckbox = 'type' in col && col.type === 'checkbox';\n @let isIndex = 'type' in col && col.type === 'index';\n @let stickyLeft = vm.stickyOffset(col.key, 'left');\n @let stickyRight = vm.stickyOffset(col.key, 'right');\n @let isLeft = vm.isStickyLeft(col.key);\n @let isRight = vm.isStickyRight(col.key);\n <div\n class=\"re-dg-cell\"\n role=\"cell\"\n [class.expanded]=\"!!col.expandBy\"\n [class]=\"cellClass(col, $any(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.height.px]=\"rowH\"\n [style.width.px]=\"vm.widthByKey(col.key)\"\n [attr.tabindex]=\"isCheckbox || isIndex ? -1 : 0\"\n (mouseenter)=\"!isCheckbox && !isIndex && showTooltip($event, $any(row), col, -1)\"\n (mouseleave)=\"!isCheckbox && !isIndex && hideTooltip()\"\n (click)=\"!isCheckbox && !isIndex && onCellClick($any(row), col, -1, $event);\"\n (contextmenu)=\"!isCheckbox && !isIndex && onCellContext($any(row), col, -1, $event)\"\n (dblclick)=\"!isCheckbox && !isIndex && onCellDoubleClick($any(row), col, -1, $event)\"\n (keydown.enter)=\"!isCheckbox && !isIndex && onCellClick($any(row), col, -1, $event)\"\n >\n @if (!isCheckbox && !isIndex) {\n <ng-container\n [ngTemplateOutlet]=\"dataCellContent\"\n [ngTemplateOutletContext]=\"{ $implicit: $any(row), col: col, index: -1 }\"\n />\n }\n </div>\n }\n </ng-template>\n\r\n @if (deferTooltip()) {\r\n @defer (when true) {\r\n @let tooltipStateValue = tooltipState();\r\n <div\r\n class=\"re-dg-tooltip\"\r\n #tooltip\r\n [class.visible]=\"tooltipStateValue.visible\"\r\n [style.left.px]=\"tooltipStateValue.x\"\r\n [style.top.px]=\"tooltipStateValue.y\"\r\n >\r\n @if (tooltipStateValue.tpl) {\r\n <ng-container\r\n [ngTemplateOutlet]=\"tooltipStateValue.tpl\"\r\n [ngTemplateOutletContext]=\"tooltipStateValue.ctx\"\r\n />\r\n } @else {\r\n {{ tooltipStateValue.text }}\r\n }\r\n </div>\r\n }\r\n } @else {\r\n @let tooltipStateValue = tooltipState();\r\n <div\r\n class=\"re-dg-tooltip\"\r\n #tooltip\r\n [class.visible]=\"tooltipStateValue.visible\"\r\n [style.left.px]=\"tooltipStateValue.x\"\r\n [style.top.px]=\"tooltipStateValue.y\"\r\n >\r\n @if (tooltipStateValue.tpl) {\r\n <ng-container\r\n [ngTemplateOutlet]=\"tooltipStateValue.tpl\"\r\n [ngTemplateOutletContext]=\"tooltipStateValue.ctx\"\r\n />\r\n } @else {\r\n {{ tooltipStateValue.text }}\r\n }\r\n </div>\r\n }\r\n\r\n <!-- Overlay scrollbar -->\r\n <div class=\"re-dg-scrollbar\" [class.visible]=\"vm.scrollbarVisible()\">\r\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-rounded: var(--re-data-grid-rounded);--re-data-grid-header-surface: #fff;--re-data-grid-header-body-gap: 0px;--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-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-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-row-hover-surface: var(--re-data-grid-cell-surface);--re-data-grid-row-hover-color: var(--re-data-grid-cell-color);--re-data-grid-row-hover-rounded: 0px;--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-row-odd-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;min-width: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-width:0;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-root.lock-vertical-scroll .re-dg-body{overflow-x:auto;overflow-y:hidden}.re-dg-root.lock-vertical-scroll .re-dg-scrollbar{display:none!important}.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;padding-bottom:var(--re-data-grid-header-body-gap)}.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{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-row:nth-child(odd) .re-dg-cell.sticky-left,.re-dg-row:nth-child(odd) .re-dg-cell.sticky-right{background-color:var(--re-data-grid-sticky-cell-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-rows>.re-dg-row:first-child .re-dg-header-cell:first-child{border-radius:var(--re-data-grid-header-rounded) 0 0 0}.re-dg-header-rows>.re-dg-row:first-child .re-dg-header-cell:last-child{border-radius:0 var(--re-data-grid-header-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)}.re-dg-row.re-dg-pinned>.re-dg-cell.sticky-left,.re-dg-row.re-dg-pinned>.re-dg-cell.sticky-right{background-color:var(--re-data-grid-pinned-surface)}.re-dg-row:hover>.re-dg-cell,.re-dg-row:hover>.re-dg-cell.sticky-left,.re-dg-row:hover>.re-dg-cell.sticky-right{background-color:var(--re-data-grid-row-hover-surface)!important;color:var(--re-data-grid-row-hover-color)!important}.re-dg-row:hover>.re-dg-cell:first-child{border-radius:var(--re-data-grid-row-hover-rounded) 0 0 var(--re-data-grid-row-hover-rounded)}.re-dg-row:hover>.re-dg-cell:last-child{border-radius:0 var(--re-data-grid-row-hover-rounded) var(--re-data-grid-row-hover-rounded) 0}.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-icon-placeholder{display:inline-block;width:1rem;height:1rem}.re-dg-cell-deferred{display:block;width:100%;height:100%}.re-dg-deferred-placeholder{background:transparent}.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, deferBlockDependencies: [() => [CheckboxIcon], () => [SortIcon], () => [ExpandIcon], () => [NgTemplateOutlet], () => [NgTemplateOutlet], () => [DataGridCellComponent], () => [NgTemplateOutlet], () => [NgTemplateOutlet], () => [NgTemplateOutlet]] });
|
|
2606
|
-
}
|
|
2607
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.1", ngImport: i0, type: DataGrid, decorators: [{
|
|
2608
|
-
type: Component,
|
|
2609
|
-
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 [class.lock-vertical-scroll]=\"lockVerticalScroll()\"\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 <ng-template #headerContent>\r\n <div\r\n class=\"re-dg-header\"\r\n role=\"rowgroup\"\r\n [style.width.px]=\"vm.contentWidth()\"\r\n [style.min-width.%]=\"100\"\r\n >\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 @if (deferIcons()) {\r\n @defer (when true) {\r\n <re-checkbox-ic [state]=\"selector.isAllSelected()\" (click)=\"selector.selectAll()\" />\r\n } @placeholder {\r\n <span class=\"re-dg-icon-placeholder\"></span>\r\n } @loading {\r\n <span class=\"re-dg-icon-placeholder\"></span>\r\n }\r\n } @else {\r\n <re-checkbox-ic [state]=\"selector.isAllSelected()\" (click)=\"selector.selectAll()\" />\r\n }\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 @if (deferIcons()) {\r\n @defer (when true) {\r\n <re-sort-ic [direction]=\"direction\" />\r\n } @placeholder {\r\n <span class=\"re-dg-icon-placeholder\"></span>\r\n } @loading {\r\n <span class=\"re-dg-icon-placeholder\"></span>\r\n }\r\n } @else {\r\n <re-sort-ic [direction]=\"direction\" />\r\n }\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 @if (deferIcons()) {\r\n @defer (when true) {\r\n <re-expand-ic [expanded]=\"expanded\" />\r\n } @placeholder {\r\n <span class=\"re-dg-icon-placeholder\"></span>\r\n } @loading {\r\n <span class=\"re-dg-icon-placeholder\"></span>\r\n }\r\n } @else {\r\n <re-expand-ic [expanded]=\"expanded\" />\r\n }\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 <ng-template #pinnedTopContent>\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 <ng-container\r\n [ngTemplateOutlet]=\"pinnedRowCells\"\r\n [ngTemplateOutletContext]=\"{ $implicit: resolvePinnedData(pr) }\"\r\n />\r\n }\r\n </div>\r\n }\r\n </ng-template>\r\n\r\n @if (deferPinned()) {\r\n @defer (when true) {\r\n <ng-container [ngTemplateOutlet]=\"pinnedTopContent\" />\r\n } @placeholder {\r\n <div class=\"re-dg-row re-dg-pinned re-dg-top re-dg-deferred-placeholder\" [style.min-height.px]=\"rowH\"></div>\r\n } @loading {\r\n <div class=\"re-dg-row re-dg-pinned re-dg-top re-dg-deferred-placeholder\" [style.min-height.px]=\"rowH\"></div>\r\n }\r\n } @else {\r\n <ng-container [ngTemplateOutlet]=\"pinnedTopContent\" />\r\n }\r\n }\r\n </div>\r\n </ng-template>\r\n\r\n @if (deferHeader()) {\r\n @defer (when true) {\r\n <ng-container [ngTemplateOutlet]=\"headerContent\" />\r\n } @placeholder {\r\n <div class=\"re-dg-header re-dg-deferred-placeholder\" [style.min-height.px]=\"headerHeight()\"></div>\r\n } @loading {\r\n <div class=\"re-dg-header re-dg-deferred-placeholder\" [style.min-height.px]=\"headerHeight()\"></div>\r\n }\r\n } @else {\r\n <ng-container [ngTemplateOutlet]=\"headerContent\" />\r\n }\r\n\r\n <ng-template #dataCellContent let-row let-col=\"col\" let-index=\"index\">\r\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 @if (deferCells()) {\r\n @defer (when true) {\r\n <re-data-grid-cell [index]=\"index\" [item]=\"row\" [column]=\"col\" />\r\n } @placeholder {\r\n <span class=\"re-dg-cell-deferred\"></span>\r\n } @loading {\r\n <span class=\"re-dg-cell-deferred\"></span>\r\n }\r\n } @else {\r\n <re-data-grid-cell [index]=\"index\" [item]=\"row\" [column]=\"col\" />\r\n }\r\n }\r\n </ng-template>\r\n\r\n <ng-template #gridContent>\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\r\n class=\"re-dg-cell\"\r\n role=\"cell\"\r\n [class.expanded]=\"!!col.expandBy\"\r\n [class]=\"cellClass(col, stickyRow)\"\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.justify-items]=\"col.align || 'left'\"\r\n [style.text-align]=\"col.align || 'left'\"\r\n [style.width.px]=\"vm.widthByKey(col.key)\"\r\n [attr.tabindex]=\"0\"\r\n (mouseenter)=\"showTooltip($event, stickyRow, col, stickyIndex)\"\r\n (mouseleave)=\"hideTooltip()\"\r\n (click)=\"onCellClick(stickyRow, col, stickyIndex, $event);\"\r\n (contextmenu)=\"onCellContext(stickyRow, col, stickyIndex, $event)\"\r\n (dblclick)=\"onCellDoubleClick(stickyRow, col, stickyIndex, $event)\"\r\n (keydown.enter)=\"onCellClick(stickyRow, col, stickyIndex, $event)\"\r\n >\r\n <ng-container\r\n [ngTemplateOutlet]=\"dataCellContent\"\r\n [ngTemplateOutletContext]=\"{ $implicit: stickyRow, col: col, index: stickyIndex }\"\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\r\n <div\r\n class=\"re-dg-cell\"\r\n role=\"cell\"\r\n [class.expanded]=\"!!col.expandBy\"\r\n [class]=\"cellClass(col, row)\"\r\n [class.sticky-left]=\"isLeft\"\r\n [class.sticky-right]=\"isRight\"\r\n [style.left.px]=\"stickyLeft\"\r\n [style.right.px]=\"stickyRight\"\r\n [style.justify-items]=\"col.align || 'left'\"\r\n [style.text-align]=\"col.align || 'left'\"\r\n [style.width.px]=\"vm.widthByKey(col.key)\"\r\n [attr.tabindex]=\"0\"\r\n (mouseenter)=\"showTooltip($event, row, col, rowIndex)\"\r\n (mouseleave)=\"hideTooltip()\"\r\n (click)=\"onCellClick(row, col, rowIndex, $event);\"\r\n (contextmenu)=\"onCellContext(row, col, rowIndex, $event)\"\r\n (dblclick)=\"onCellDoubleClick(row, col, rowIndex, $event)\"\r\n (keydown.enter)=\"onCellClick(row, col, rowIndex, $event)\"\r\n >\r\n <ng-container\r\n [ngTemplateOutlet]=\"dataCellContent\"\r\n [ngTemplateOutletContext]=\"{ $implicit: row, col: col, index: rowIndex }\"\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 <ng-template #pinnedBottomContent>\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 <ng-container\r\n [ngTemplateOutlet]=\"pinnedRowCells\"\r\n [ngTemplateOutletContext]=\"{ $implicit: resolvePinnedData(pr) }\"\r\n />\r\n }\r\n </div>\r\n }\r\n </div>\r\n </ng-template>\r\n\r\n @if (deferPinned()) {\r\n @defer (when true) {\r\n <ng-container [ngTemplateOutlet]=\"pinnedBottomContent\" />\r\n } @placeholder {\r\n <div class=\"re-dg-footer re-dg-deferred-placeholder\" [style.min-height.px]=\"rowH\"></div>\r\n } @loading {\r\n <div class=\"re-dg-footer re-dg-deferred-placeholder\" [style.min-height.px]=\"rowH\"></div>\r\n }\r\n } @else {\r\n <ng-container [ngTemplateOutlet]=\"pinnedBottomContent\" />\r\n }\r\n }\r\n </ng-template>\r\n\r\n @if (deferContent()) {\r\n @defer (when true) {\r\n <ng-container [ngTemplateOutlet]=\"gridContent\" />\r\n } @placeholder {\r\n <div class=\"re-dg-deferred-placeholder\" [style.min-height.px]=\"rowH * 3\"></div>\r\n } @loading {\r\n <div class=\"re-dg-deferred-placeholder\" [style.min-height.px]=\"rowH * 3\"></div>\r\n }\r\n } @else {\r\n <ng-container [ngTemplateOutlet]=\"gridContent\" />\r\n }\r\n </div>\r\n\r\n <ng-template #pinnedRowCells let-row>\n @for (col of cols; track col.key) {\n @let isCheckbox = 'type' in col && col.type === 'checkbox';\n @let isIndex = 'type' in col && col.type === 'index';\n @let stickyLeft = vm.stickyOffset(col.key, 'left');\n @let stickyRight = vm.stickyOffset(col.key, 'right');\n @let isLeft = vm.isStickyLeft(col.key);\n @let isRight = vm.isStickyRight(col.key);\n <div\n class=\"re-dg-cell\"\n role=\"cell\"\n [class.expanded]=\"!!col.expandBy\"\n [class]=\"cellClass(col, $any(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.height.px]=\"rowH\"\n [style.width.px]=\"vm.widthByKey(col.key)\"\n [attr.tabindex]=\"isCheckbox || isIndex ? -1 : 0\"\n (mouseenter)=\"!isCheckbox && !isIndex && showTooltip($event, $any(row), col, -1)\"\n (mouseleave)=\"!isCheckbox && !isIndex && hideTooltip()\"\n (click)=\"!isCheckbox && !isIndex && onCellClick($any(row), col, -1, $event);\"\n (contextmenu)=\"!isCheckbox && !isIndex && onCellContext($any(row), col, -1, $event)\"\n (dblclick)=\"!isCheckbox && !isIndex && onCellDoubleClick($any(row), col, -1, $event)\"\n (keydown.enter)=\"!isCheckbox && !isIndex && onCellClick($any(row), col, -1, $event)\"\n >\n @if (!isCheckbox && !isIndex) {\n <ng-container\n [ngTemplateOutlet]=\"dataCellContent\"\n [ngTemplateOutletContext]=\"{ $implicit: $any(row), col: col, index: -1 }\"\n />\n }\n </div>\n }\n </ng-template>\n\r\n @if (deferTooltip()) {\r\n @defer (when true) {\r\n @let tooltipStateValue = tooltipState();\r\n <div\r\n class=\"re-dg-tooltip\"\r\n #tooltip\r\n [class.visible]=\"tooltipStateValue.visible\"\r\n [style.left.px]=\"tooltipStateValue.x\"\r\n [style.top.px]=\"tooltipStateValue.y\"\r\n >\r\n @if (tooltipStateValue.tpl) {\r\n <ng-container\r\n [ngTemplateOutlet]=\"tooltipStateValue.tpl\"\r\n [ngTemplateOutletContext]=\"tooltipStateValue.ctx\"\r\n />\r\n } @else {\r\n {{ tooltipStateValue.text }}\r\n }\r\n </div>\r\n }\r\n } @else {\r\n @let tooltipStateValue = tooltipState();\r\n <div\r\n class=\"re-dg-tooltip\"\r\n #tooltip\r\n [class.visible]=\"tooltipStateValue.visible\"\r\n [style.left.px]=\"tooltipStateValue.x\"\r\n [style.top.px]=\"tooltipStateValue.y\"\r\n >\r\n @if (tooltipStateValue.tpl) {\r\n <ng-container\r\n [ngTemplateOutlet]=\"tooltipStateValue.tpl\"\r\n [ngTemplateOutletContext]=\"tooltipStateValue.ctx\"\r\n />\r\n } @else {\r\n {{ tooltipStateValue.text }}\r\n }\r\n </div>\r\n }\r\n\r\n <!-- Overlay scrollbar -->\r\n <div class=\"re-dg-scrollbar\" [class.visible]=\"vm.scrollbarVisible()\">\r\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-rounded: var(--re-data-grid-rounded);--re-data-grid-header-surface: #fff;--re-data-grid-header-body-gap: 0px;--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-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-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-row-hover-surface: var(--re-data-grid-cell-surface);--re-data-grid-row-hover-color: var(--re-data-grid-cell-color);--re-data-grid-row-hover-rounded: 0px;--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-row-odd-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;min-width: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-width:0;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-root.lock-vertical-scroll .re-dg-body{overflow-x:auto;overflow-y:hidden}.re-dg-root.lock-vertical-scroll .re-dg-scrollbar{display:none!important}.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;padding-bottom:var(--re-data-grid-header-body-gap)}.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{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-row:nth-child(odd) .re-dg-cell.sticky-left,.re-dg-row:nth-child(odd) .re-dg-cell.sticky-right{background-color:var(--re-data-grid-sticky-cell-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-rows>.re-dg-row:first-child .re-dg-header-cell:first-child{border-radius:var(--re-data-grid-header-rounded) 0 0 0}.re-dg-header-rows>.re-dg-row:first-child .re-dg-header-cell:last-child{border-radius:0 var(--re-data-grid-header-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)}.re-dg-row.re-dg-pinned>.re-dg-cell.sticky-left,.re-dg-row.re-dg-pinned>.re-dg-cell.sticky-right{background-color:var(--re-data-grid-pinned-surface)}.re-dg-row:hover>.re-dg-cell,.re-dg-row:hover>.re-dg-cell.sticky-left,.re-dg-row:hover>.re-dg-cell.sticky-right{background-color:var(--re-data-grid-row-hover-surface)!important;color:var(--re-data-grid-row-hover-color)!important}.re-dg-row:hover>.re-dg-cell:first-child{border-radius:var(--re-data-grid-row-hover-rounded) 0 0 var(--re-data-grid-row-hover-rounded)}.re-dg-row:hover>.re-dg-cell:last-child{border-radius:0 var(--re-data-grid-row-hover-rounded) var(--re-data-grid-row-hover-rounded) 0}.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-icon-placeholder{display:inline-block;width:1rem;height:1rem}.re-dg-cell-deferred{display:block;width:100%;height:100%}.re-dg-deferred-placeholder{background:transparent}.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"] }]
|
|
2610
|
-
}], 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 }] }], lockVerticalScroll: [{ type: i0.Input, args: [{ isSignal: true, alias: "lockVerticalScroll", 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 }] }], deferContent: [{ type: i0.Input, args: [{ isSignal: true, alias: "deferContent", required: false }] }], deferHeader: [{ type: i0.Input, args: [{ isSignal: true, alias: "deferHeader", required: false }] }], deferPinned: [{ type: i0.Input, args: [{ isSignal: true, alias: "deferPinned", required: false }] }], deferCells: [{ type: i0.Input, args: [{ isSignal: true, alias: "deferCells", required: false }] }], deferIcons: [{ type: i0.Input, args: [{ isSignal: true, alias: "deferIcons", required: false }] }], deferTooltip: [{ type: i0.Input, args: [{ isSignal: true, alias: "deferTooltip", 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 }] }] } });
|
|
2611
|
-
|
|
2612
|
-
/**
|
|
2613
|
-
* Generated bundle index. Do not edit.
|
|
2614
|
-
*/
|
|
2615
|
-
|
|
2616
|
-
export { DATA_GRID_CONFIG, DEFAULT_DATA_GRID_DEFAULTS, DataGrid, DataGridCellEmptyDirective, DataGridCellLoadingDirective, DataGridCellTemplateDirective, DataGridDeclarativeCellDirective, DataGridDeclarativeColumn, DataGridDeclarativeHeaderDirective, DataGridExpanderIconDirective, DataGridHeaderTemplateDirective, DataGridRowDirective, DataGridSortIconDirective, DataGridStickyRowDirective, DataGridTypeCellTemplateDirective, provideDataGridDefaults };
|
|
1
|
+
export { o as DATA_GRID_CONFIG, p as DEFAULT_DATA_GRID_DEFAULTS, r as DataGrid, i as DataGridCellEmptyDirective, j as DataGridCellLoadingDirective, b as DataGridCellTemplateDirective, h as DataGridDeclarativeCellDirective, f as DataGridDeclarativeColumn, g as DataGridDeclarativeHeaderDirective, n as DataGridExpanderIconDirective, d as DataGridHeaderTemplateDirective, e as DataGridRowDirective, l as DataGridSortIconDirective, k as DataGridStickyRowDirective, D as DataGridTypeCellTemplateDirective, q as provideDataGridDefaults } from './reforgium-data-grid-reforgium-data-grid-Dn9s4YO5.mjs';
|
|
2617
2
|
//# sourceMappingURL=reforgium-data-grid.mjs.map
|