@reforgium/data-grid 1.0.2 → 1.1.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.
|
@@ -1,49 +1,247 @@
|
|
|
1
1
|
import * as i0 from '@angular/core';
|
|
2
|
-
import { input, inject, TemplateRef, Directive,
|
|
2
|
+
import { input, inject, TemplateRef, Directive, numberAttribute, output, computed, Component, signal, effect, Injectable, viewChild, ElementRef, ChangeDetectionStrategy, booleanAttribute, contentChildren, afterRenderEffect, DestroyRef, untracked } from '@angular/core';
|
|
3
3
|
import { NgTemplateOutlet, DatePipe, DecimalPipe } from '@angular/common';
|
|
4
4
|
import { Subscription, fromEvent, auditTime } from 'rxjs';
|
|
5
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 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
|
+
*/
|
|
6
23
|
class DataGridTypeCellTemplateDirective {
|
|
7
|
-
|
|
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
|
+
*/
|
|
8
55
|
tpl = inject((TemplateRef));
|
|
9
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.
|
|
10
|
-
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "21.
|
|
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 });
|
|
11
58
|
}
|
|
12
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.
|
|
59
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.1", ngImport: i0, type: DataGridCellTemplateDirective, decorators: [{
|
|
13
60
|
type: Directive,
|
|
14
|
-
args: [{ selector: 'ng-template[
|
|
15
|
-
}], propDecorators: {
|
|
61
|
+
args: [{ selector: 'ng-template[reDataGridCell]' }]
|
|
62
|
+
}], propDecorators: { key: [{ type: i0.Input, args: [{ isSignal: true, alias: "reDataGridCell", required: false }] }] } });
|
|
16
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
|
+
*/
|
|
17
78
|
class DataGridHeaderTemplateDirective {
|
|
18
|
-
|
|
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
|
+
*/
|
|
19
89
|
tpl = inject((TemplateRef));
|
|
20
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.
|
|
21
|
-
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "21.
|
|
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 });
|
|
22
92
|
}
|
|
23
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.
|
|
93
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.1", ngImport: i0, type: DataGridHeaderTemplateDirective, decorators: [{
|
|
24
94
|
type: Directive,
|
|
25
|
-
args: [{ selector: 'ng-template[
|
|
26
|
-
}], propDecorators: { key: [{ type: i0.Input, args: [{ isSignal: true, alias: "
|
|
95
|
+
args: [{ selector: 'ng-template[reDataGridHeader]' }]
|
|
96
|
+
}], propDecorators: { key: [{ type: i0.Input, args: [{ isSignal: true, alias: "reDataGridHeader", required: false }] }] } });
|
|
27
97
|
|
|
98
|
+
/**
|
|
99
|
+
* Directive for providing a custom template to display when the data grid has no data.
|
|
100
|
+
*
|
|
101
|
+
* Used as a structural directive on `<ng-template>` elements within data grid components.
|
|
102
|
+
* The template receives a boolean context value indicating the empty state.
|
|
103
|
+
*
|
|
104
|
+
* Example:
|
|
105
|
+
* ```html
|
|
106
|
+
* <ng-template reDataGridEmpty let-isEmpty>
|
|
107
|
+
* <div *ngIf="isEmpty">No data available</div>
|
|
108
|
+
* </ng-template>
|
|
109
|
+
* ```
|
|
110
|
+
*
|
|
111
|
+
* Template context:
|
|
112
|
+
* - `$implicit: boolean` — indicates whether the grid is in an empty state
|
|
113
|
+
*/
|
|
28
114
|
class DataGridCellEmptyDirective {
|
|
29
115
|
tpl = inject((TemplateRef));
|
|
30
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.
|
|
31
|
-
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "21.
|
|
116
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.1", ngImport: i0, type: DataGridCellEmptyDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive });
|
|
117
|
+
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "21.1.1", type: DataGridCellEmptyDirective, isStandalone: true, selector: "ng-template[reDataGridEmpty]", ngImport: i0 });
|
|
32
118
|
}
|
|
33
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.
|
|
119
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.1", ngImport: i0, type: DataGridCellEmptyDirective, decorators: [{
|
|
34
120
|
type: Directive,
|
|
35
|
-
args: [{ selector: 'ng-template[
|
|
121
|
+
args: [{ selector: 'ng-template[reDataGridEmpty]' }]
|
|
36
122
|
}] });
|
|
123
|
+
/**
|
|
124
|
+
* Directive for providing a custom template to display when the data grid is loading data.
|
|
125
|
+
*
|
|
126
|
+
* Used as a structural directive on `<ng-template>` elements within data grid components.
|
|
127
|
+
* The template receives a boolean context value indicating the loading state.
|
|
128
|
+
*
|
|
129
|
+
* Example:
|
|
130
|
+
* ```html
|
|
131
|
+
* <ng-template reDataGridLoading let-isLoading>
|
|
132
|
+
* <div *ngIf="isLoading">Loading...</div>
|
|
133
|
+
* </ng-template>
|
|
134
|
+
* ```
|
|
135
|
+
*
|
|
136
|
+
* Template context:
|
|
137
|
+
* - `$implicit: boolean` — indicates whether the grid is in loading state
|
|
138
|
+
*/
|
|
37
139
|
class DataGridCellLoadingDirective {
|
|
38
140
|
tpl = inject((TemplateRef));
|
|
39
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.
|
|
40
|
-
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "21.
|
|
141
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.1", ngImport: i0, type: DataGridCellLoadingDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive });
|
|
142
|
+
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "21.1.1", type: DataGridCellLoadingDirective, isStandalone: true, selector: "ng-template[reDataGridLoading]", ngImport: i0 });
|
|
143
|
+
}
|
|
144
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.1", ngImport: i0, type: DataGridCellLoadingDirective, decorators: [{
|
|
145
|
+
type: Directive,
|
|
146
|
+
args: [{ selector: 'ng-template[reDataGridLoading]' }]
|
|
147
|
+
}] });
|
|
148
|
+
|
|
149
|
+
class DataGridSortIconDirective {
|
|
150
|
+
tpl = inject((TemplateRef));
|
|
151
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.1", ngImport: i0, type: DataGridSortIconDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive });
|
|
152
|
+
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "21.1.1", type: DataGridSortIconDirective, isStandalone: true, selector: "ng-template[reDataGridSortIcon]", ngImport: i0 });
|
|
153
|
+
}
|
|
154
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.1", ngImport: i0, type: DataGridSortIconDirective, decorators: [{
|
|
155
|
+
type: Directive,
|
|
156
|
+
args: [{ selector: 'ng-template[reDataGridSortIcon]' }]
|
|
157
|
+
}] });
|
|
158
|
+
class DataGridExpanderIconDirective {
|
|
159
|
+
tpl = inject((TemplateRef));
|
|
160
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.1", ngImport: i0, type: DataGridExpanderIconDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive });
|
|
161
|
+
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "21.1.1", type: DataGridExpanderIconDirective, isStandalone: true, selector: "ng-template[reDataGridExpanderIcon]", ngImport: i0 });
|
|
41
162
|
}
|
|
42
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.
|
|
163
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.1", ngImport: i0, type: DataGridExpanderIconDirective, decorators: [{
|
|
43
164
|
type: Directive,
|
|
44
|
-
args: [{ selector: 'ng-template[
|
|
165
|
+
args: [{ selector: 'ng-template[reDataGridExpanderIcon]' }]
|
|
45
166
|
}] });
|
|
46
167
|
|
|
168
|
+
// noinspection CssUnresolvedCustomProperty
|
|
169
|
+
class DataGridPaginator {
|
|
170
|
+
current = input(0, { ...(ngDevMode ? { debugName: "current" } : {}), transform: numberAttribute });
|
|
171
|
+
totalElements = input(0, { ...(ngDevMode ? { debugName: "totalElements" } : {}), transform: numberAttribute });
|
|
172
|
+
pageSize = input(0, { ...(ngDevMode ? { debugName: "pageSize" } : {}), transform: numberAttribute });
|
|
173
|
+
maxShowPages = input(7, { ...(ngDevMode ? { debugName: "maxShowPages" } : {}), transform: numberAttribute });
|
|
174
|
+
pageChange = output();
|
|
175
|
+
totalPages = computed(() => {
|
|
176
|
+
const size = this.pageSize();
|
|
177
|
+
return size > 0 ? Math.ceil(this.totalElements() / size) : 0;
|
|
178
|
+
}, ...(ngDevMode ? [{ debugName: "totalPages" }] : []));
|
|
179
|
+
pages = computed(() => {
|
|
180
|
+
const current = this.current();
|
|
181
|
+
const total = this.totalPages();
|
|
182
|
+
const max = this.maxShowPages();
|
|
183
|
+
if (total <= max) {
|
|
184
|
+
return Array.from({ length: total }, (_, i) => i);
|
|
185
|
+
}
|
|
186
|
+
const pages = [];
|
|
187
|
+
const sidePages = Math.floor((max - 3) / 2);
|
|
188
|
+
pages.push(0);
|
|
189
|
+
let start = Math.max(1, current - sidePages);
|
|
190
|
+
let end = Math.min(total - 2, current + sidePages);
|
|
191
|
+
if (current <= sidePages + 1) {
|
|
192
|
+
end = max - 3;
|
|
193
|
+
}
|
|
194
|
+
else if (current >= total - sidePages - 2) {
|
|
195
|
+
start = total - max + 2;
|
|
196
|
+
}
|
|
197
|
+
if (start > 1) {
|
|
198
|
+
pages.push(-1);
|
|
199
|
+
}
|
|
200
|
+
for (let i = start; i <= end; i++) {
|
|
201
|
+
pages.push(i);
|
|
202
|
+
}
|
|
203
|
+
if (end < total - 2) {
|
|
204
|
+
pages.push(-1);
|
|
205
|
+
}
|
|
206
|
+
pages.push(total - 1);
|
|
207
|
+
return pages;
|
|
208
|
+
}, ...(ngDevMode ? [{ debugName: "pages" }] : []));
|
|
209
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.1", ngImport: i0, type: DataGridPaginator, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
210
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.1.1", type: DataGridPaginator, isStandalone: true, selector: "re-data-grid-paginator", inputs: { current: { classPropertyName: "current", publicName: "current", isSignal: true, isRequired: false, transformFunction: null }, totalElements: { classPropertyName: "totalElements", publicName: "totalElements", isSignal: true, isRequired: false, transformFunction: null }, pageSize: { classPropertyName: "pageSize", publicName: "pageSize", isSignal: true, isRequired: false, transformFunction: null }, maxShowPages: { classPropertyName: "maxShowPages", publicName: "maxShowPages", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { pageChange: "pageChange" }, ngImport: i0, template: `
|
|
211
|
+
@for (page of pages(); track $index) {
|
|
212
|
+
@if (page === -1) {
|
|
213
|
+
<span class="re-paginator-gap">...</span>
|
|
214
|
+
} @else {
|
|
215
|
+
<button
|
|
216
|
+
class="re-paginator-page"
|
|
217
|
+
[class.re-paginator-page--active]="page === current()"
|
|
218
|
+
(click)="pageChange.emit(page)"
|
|
219
|
+
>
|
|
220
|
+
{{ page + 1 }}
|
|
221
|
+
</button>
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
`, isInline: true, styles: [":host{--re-data-grid-paginator-gap: .5rem;--re-data-grid-paginator-page-size: 1.75rem;--re-data-grid-paginator-page-border: 1px solid var(--re-data-grid-paginator-separator-color, #e2e8f0);--re-data-grid-paginator-page-separator-color: var(--re-data-grid-separator-color, --border-color);--re-data-grid-paginator-page-rounded: var(--re-data-grid-rounded, --radius-md);--re-data-grid-paginator-page-surface: var(--re-data-grid-surface, white);--re-data-grid-paginator-page-color: var(--text-primary, #1e293b);--re-data-grid-paginator-page-font-size: .875rem;--re-data-grid-paginator-page-active-surface: var(--re-data-grid-active, #3b82f6);--re-data-grid-paginator-page-active-color: white;--re-data-grid-paginator-page-hover-surface: var(--re-data-grid-active, #3b82f6);--re-data-grid-paginator-page-hover-color: white;display:flex;align-items:center;gap:var(--re-data-grid-paginator-gap)}.re-paginator-page{display:flex;justify-content:center;align-items:center;width:var(--re-data-grid-paginator-page-size);height:var(--re-data-grid-paginator-page-size);border-radius:var(--re-data-grid-paginator-page-rounded);border:var(--re-data-grid-paginator-page-border);font-size:var(--re-data-grid-paginator-page-font-size);background:var(--re-data-grid-paginator-page-surface);color:var(--re-data-grid-paginator-page-color);cursor:pointer;transition:all .2s}.re-paginator-page:hover:not(.re-paginator-page--active){background:var(--re-data-grid-paginator-page-hover-surface);color:var(--re-data-grid-paginator-page-hover-color)}.re-paginator-page--active{border-color:var(--re-data-grid-paginator-active-surface);background:var(--re-data-grid-paginator-page-active-surface);color:var(--re-data-grid-paginator-page-active-color)}.re-paginator-gap{display:flex;align-items:center;justify-content:center;padding:var(--re-data-grid-paginator-page-paddings);color:var(--re-data-grid-paginator-page-color)}\n"] });
|
|
225
|
+
}
|
|
226
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.1", ngImport: i0, type: DataGridPaginator, decorators: [{
|
|
227
|
+
type: Component,
|
|
228
|
+
args: [{ selector: 're-data-grid-paginator', template: `
|
|
229
|
+
@for (page of pages(); track $index) {
|
|
230
|
+
@if (page === -1) {
|
|
231
|
+
<span class="re-paginator-gap">...</span>
|
|
232
|
+
} @else {
|
|
233
|
+
<button
|
|
234
|
+
class="re-paginator-page"
|
|
235
|
+
[class.re-paginator-page--active]="page === current()"
|
|
236
|
+
(click)="pageChange.emit(page)"
|
|
237
|
+
>
|
|
238
|
+
{{ page + 1 }}
|
|
239
|
+
</button>
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
`, styles: [":host{--re-data-grid-paginator-gap: .5rem;--re-data-grid-paginator-page-size: 1.75rem;--re-data-grid-paginator-page-border: 1px solid var(--re-data-grid-paginator-separator-color, #e2e8f0);--re-data-grid-paginator-page-separator-color: var(--re-data-grid-separator-color, --border-color);--re-data-grid-paginator-page-rounded: var(--re-data-grid-rounded, --radius-md);--re-data-grid-paginator-page-surface: var(--re-data-grid-surface, white);--re-data-grid-paginator-page-color: var(--text-primary, #1e293b);--re-data-grid-paginator-page-font-size: .875rem;--re-data-grid-paginator-page-active-surface: var(--re-data-grid-active, #3b82f6);--re-data-grid-paginator-page-active-color: white;--re-data-grid-paginator-page-hover-surface: var(--re-data-grid-active, #3b82f6);--re-data-grid-paginator-page-hover-color: white;display:flex;align-items:center;gap:var(--re-data-grid-paginator-gap)}.re-paginator-page{display:flex;justify-content:center;align-items:center;width:var(--re-data-grid-paginator-page-size);height:var(--re-data-grid-paginator-page-size);border-radius:var(--re-data-grid-paginator-page-rounded);border:var(--re-data-grid-paginator-page-border);font-size:var(--re-data-grid-paginator-page-font-size);background:var(--re-data-grid-paginator-page-surface);color:var(--re-data-grid-paginator-page-color);cursor:pointer;transition:all .2s}.re-paginator-page:hover:not(.re-paginator-page--active){background:var(--re-data-grid-paginator-page-hover-surface);color:var(--re-data-grid-paginator-page-hover-color)}.re-paginator-page--active{border-color:var(--re-data-grid-paginator-active-surface);background:var(--re-data-grid-paginator-page-active-surface);color:var(--re-data-grid-paginator-page-active-color)}.re-paginator-gap{display:flex;align-items:center;justify-content:center;padding:var(--re-data-grid-paginator-page-paddings);color:var(--re-data-grid-paginator-page-color)}\n"] }]
|
|
243
|
+
}], propDecorators: { current: [{ type: i0.Input, args: [{ isSignal: true, alias: "current", required: false }] }], totalElements: [{ type: i0.Input, args: [{ isSignal: true, alias: "totalElements", required: false }] }], pageSize: [{ type: i0.Input, args: [{ isSignal: true, alias: "pageSize", required: false }] }], maxShowPages: [{ type: i0.Input, args: [{ isSignal: true, alias: "maxShowPages", required: false }] }], pageChange: [{ type: i0.Output, args: ["pageChange"] }] } });
|
|
244
|
+
|
|
47
245
|
const GRID_INDEX_COLUMN = {
|
|
48
246
|
key: '_index',
|
|
49
247
|
type: 'index',
|
|
@@ -61,6 +259,38 @@ const GRID_CHECKBOX_COLUMN = {
|
|
|
61
259
|
sticky: true,
|
|
62
260
|
};
|
|
63
261
|
|
|
262
|
+
/**
|
|
263
|
+
* Calculates and distributes column widths within a container based on column configuration.
|
|
264
|
+
*
|
|
265
|
+
* The function implements a flexible layout algorithm that:
|
|
266
|
+
* - Respects fixed-width columns (with `width` property set)
|
|
267
|
+
* - Distributes remaining space among auto-sized columns based on their `flex` ratios
|
|
268
|
+
* - Enforces `minWidth` and `maxWidth` constraints
|
|
269
|
+
* - Uses a "water-fill" algorithm to grow columns proportionally
|
|
270
|
+
* - Handles rounding errors to ensure total width matches container width
|
|
271
|
+
*
|
|
272
|
+
* @param columns - Array of column configuration objects with properties
|
|
273
|
+
* like `visible`, `width`, `minWidth`, `maxWidth`, `flex`, and `key`
|
|
274
|
+
* @param containerWidth - Available width in pixels for all columns
|
|
275
|
+
* @param defaultCol - Default minimum width for auto-sized columns without explicit `minWidth`
|
|
276
|
+
* @returns Layout result containing computed widths array, total width, and width lookup by column key
|
|
277
|
+
*
|
|
278
|
+
* @example
|
|
279
|
+
* ```typescript
|
|
280
|
+
* const result = layoutColumns(
|
|
281
|
+
* [
|
|
282
|
+
* { key: 'id', width: 50, visible: true },
|
|
283
|
+
* { key: 'name', minWidth: 100, flex: 2, visible: true },
|
|
284
|
+
* { key: 'age', minWidth: 80, flex: 1, visible: true }
|
|
285
|
+
* ],
|
|
286
|
+
* 800,
|
|
287
|
+
* 100
|
|
288
|
+
* );
|
|
289
|
+
* // result.widths = [50, 500, 250]
|
|
290
|
+
* // result.total = 800
|
|
291
|
+
* // result.byKey = { id: 50, name: 500, age: 250 }
|
|
292
|
+
* ```
|
|
293
|
+
*/
|
|
64
294
|
function layoutColumns(columns, containerWidth, defaultCol) {
|
|
65
295
|
const idx = visibleIndexes(columns);
|
|
66
296
|
const { fixedIdx, autoIdx } = splitFixed(columns, idx);
|
|
@@ -94,6 +324,14 @@ function layoutColumns(columns, containerWidth, defaultCol) {
|
|
|
94
324
|
}
|
|
95
325
|
return { widths, total, byKey };
|
|
96
326
|
}
|
|
327
|
+
/**
|
|
328
|
+
* Filters and returns indexes of visible columns.
|
|
329
|
+
*
|
|
330
|
+
* A column is considered visible if its `visible` property is not explicitly set to `false`.
|
|
331
|
+
*
|
|
332
|
+
* @param cols - Array of column configurations
|
|
333
|
+
* @returns Array of indexes for columns that should be displayed
|
|
334
|
+
*/
|
|
97
335
|
function visibleIndexes(cols) {
|
|
98
336
|
const out = [];
|
|
99
337
|
for (let i = 0; i < cols.length; i++) {
|
|
@@ -102,6 +340,16 @@ function visibleIndexes(cols) {
|
|
|
102
340
|
}
|
|
103
341
|
return out;
|
|
104
342
|
}
|
|
343
|
+
/**
|
|
344
|
+
* Splits visible column indexes into fixed-width and auto-sized groups.
|
|
345
|
+
*
|
|
346
|
+
* Fixed columns have an explicit `width` property defined.
|
|
347
|
+
* Auto-sized columns have `width` as `undefined` and will grow to fill available space.
|
|
348
|
+
*
|
|
349
|
+
* @param cols - Array of column configurations
|
|
350
|
+
* @param visibleIdx - Array of visible column indexes
|
|
351
|
+
* @returns Object with `fixedIdx` (fixed-width column indexes) and `autoIdx` (auto-sized column indexes)
|
|
352
|
+
*/
|
|
105
353
|
function splitFixed(cols, visibleIdx) {
|
|
106
354
|
const fixedIdx = [];
|
|
107
355
|
const autoIdx = [];
|
|
@@ -110,16 +358,53 @@ function splitFixed(cols, visibleIdx) {
|
|
|
110
358
|
}
|
|
111
359
|
return { fixedIdx, autoIdx };
|
|
112
360
|
}
|
|
361
|
+
/**
|
|
362
|
+
* Computes actual widths for fixed-width columns, respecting min/max constraints.
|
|
363
|
+
*
|
|
364
|
+
* Takes the explicit `width` value and clamps it between `minWidth` and `maxWidth`.
|
|
365
|
+
*
|
|
366
|
+
* @param cols - Array of column configurations
|
|
367
|
+
* @param fixedIdx - Array of fixed-width column indexes
|
|
368
|
+
* @returns Array of objects with column index and computed width
|
|
369
|
+
*/
|
|
113
370
|
const computeFixedWidths = (cols, fixedIdx) => fixedIdx.map((index) => {
|
|
114
371
|
const col = cols[index];
|
|
115
372
|
const width = clamp$1(col.width, col.minWidth, col.maxWidth);
|
|
116
373
|
return { index, width };
|
|
117
374
|
});
|
|
375
|
+
/**
|
|
376
|
+
* Computes base (minimum) widths for auto-sized columns before distribution of free space.
|
|
377
|
+
*
|
|
378
|
+
* Uses column's `minWidth` if specified, otherwise falls back to the default value.
|
|
379
|
+
* The base width is clamped between `minWidth` and `maxWidth` constraints.
|
|
380
|
+
*
|
|
381
|
+
* @param cols - Array of column configurations
|
|
382
|
+
* @param autoIdx - Array of auto-sized column indexes
|
|
383
|
+
* @param def - Default minimum width to use when column's `minWidth` is not specified
|
|
384
|
+
* @returns Array of objects with column index and base width
|
|
385
|
+
*/
|
|
118
386
|
const computeBaseAuto = (cols, autoIdx, def) => autoIdx.map((index) => {
|
|
119
387
|
const c = cols[index];
|
|
120
388
|
const base = clamp$1(c.minWidth ?? def, c.minWidth, c.maxWidth);
|
|
121
389
|
return { index, width: base };
|
|
122
390
|
});
|
|
391
|
+
/**
|
|
392
|
+
* Distributes free space among auto-sized columns using a "water-fill" algorithm.
|
|
393
|
+
*
|
|
394
|
+
* The algorithm works iteratively:
|
|
395
|
+
* 1. Identifies columns that can still grow (haven't reached `maxWidth`)
|
|
396
|
+
* 2. Distributes remaining space proportionally based on each column's `flex` ratio
|
|
397
|
+
* 3. Respects `maxWidth` constraints by capping growth
|
|
398
|
+
* 4. Repeats until no space remains or no columns can grow further
|
|
399
|
+
*
|
|
400
|
+
* This ensures that columns with higher `flex` values receive proportionally more space,
|
|
401
|
+
* while respecting all width constraints.
|
|
402
|
+
*
|
|
403
|
+
* @param cols - Array of column configurations
|
|
404
|
+
* @param base - Array of column indexes with their base widths before distribution
|
|
405
|
+
* @param free - Amount of free space (in pixels) to distribute among columns
|
|
406
|
+
* @returns Array of objects with column index and final width after distribution
|
|
407
|
+
*/
|
|
123
408
|
function distributeFree(cols, base, free) {
|
|
124
409
|
if (free <= 0 || base.length === 0) {
|
|
125
410
|
return base;
|
|
@@ -151,10 +436,37 @@ function distributeFree(cols, base, free) {
|
|
|
151
436
|
}
|
|
152
437
|
return widths;
|
|
153
438
|
}
|
|
439
|
+
/**
|
|
440
|
+
* Checks whether a column can grow beyond its current width.
|
|
441
|
+
*
|
|
442
|
+
* A column can grow if its current width is less than its `maxWidth` constraint
|
|
443
|
+
* (or `Infinity` if no max is specified), with a small epsilon tolerance.
|
|
444
|
+
*
|
|
445
|
+
* @param cols - Array of column configurations
|
|
446
|
+
* @param i - Column index to check
|
|
447
|
+
* @param cur - Current width of the column
|
|
448
|
+
* @returns `true` if the column can grow, `false` otherwise
|
|
449
|
+
*/
|
|
154
450
|
function widthsCanGrow(cols, i, cur) {
|
|
155
451
|
const max = cols[i].maxWidth ?? Infinity;
|
|
156
452
|
return cur < max - 1e-6;
|
|
157
453
|
}
|
|
454
|
+
/**
|
|
455
|
+
* Rounds auto-sized column widths to integers and distributes any remaining pixels to match container width exactly.
|
|
456
|
+
*
|
|
457
|
+
* This function handles rounding errors that occur when distributing fractional widths:
|
|
458
|
+
* 1. Floors all auto-sized column widths to integers
|
|
459
|
+
* 2. Calculates how many pixels are needed to reach the exact container width
|
|
460
|
+
* 3. Distributes remaining pixels one-by-one to columns (right-to-left) that haven't reached their `maxWidth`
|
|
461
|
+
*
|
|
462
|
+
* Fixed-width columns are not modified. This ensures the total width matches the container precisely.
|
|
463
|
+
*
|
|
464
|
+
* @param cols - Array of column configurations
|
|
465
|
+
* @param widths - Array of column widths to be modified in place
|
|
466
|
+
* @param fixedIdx - Array of fixed-width column indexes (not modified)
|
|
467
|
+
* @param autoIdx - Array of auto-sized column indexes (will be rounded and adjusted)
|
|
468
|
+
* @param containerWidth - Target total width that must be matched exactly
|
|
469
|
+
*/
|
|
158
470
|
function roundAndFix(cols, widths, fixedIdx, autoIdx, containerWidth) {
|
|
159
471
|
const sumFixed = fixedIdx.reduce((s, i) => s + widths[i], 0);
|
|
160
472
|
let sumAuto = 0;
|
|
@@ -175,11 +487,33 @@ function roundAndFix(cols, widths, fixedIdx, autoIdx, containerWidth) {
|
|
|
175
487
|
}
|
|
176
488
|
}
|
|
177
489
|
}
|
|
490
|
+
/**
|
|
491
|
+
* Clamps a numeric value between optional minimum and maximum bounds.
|
|
492
|
+
*
|
|
493
|
+
* If `min` is provided and the value is less than it, returns `min`.
|
|
494
|
+
* If `max` is provided and the value is greater than it, returns `max`.
|
|
495
|
+
* Otherwise returns the original value.
|
|
496
|
+
*
|
|
497
|
+
* @param v - Value to clamp
|
|
498
|
+
* @param min - Optional minimum bound
|
|
499
|
+
* @param max - Optional maximum bound
|
|
500
|
+
* @returns Clamped value
|
|
501
|
+
*/
|
|
178
502
|
function clamp$1(v, min, max) {
|
|
179
503
|
min != null && v < min && (v = min);
|
|
180
504
|
max != null && v > max && (v = max);
|
|
181
505
|
return v;
|
|
182
506
|
}
|
|
507
|
+
/**
|
|
508
|
+
* Calculates the sum of numeric values or width properties in an array.
|
|
509
|
+
*
|
|
510
|
+
* Supports two input formats:
|
|
511
|
+
* - Array of numbers: sums all numeric values
|
|
512
|
+
* - Array of objects with `width` property: sums all `width` values
|
|
513
|
+
*
|
|
514
|
+
* @param items - Array of numbers or objects with `width` property
|
|
515
|
+
* @returns Sum of all values
|
|
516
|
+
*/
|
|
183
517
|
function sum(items) {
|
|
184
518
|
if (Array.isArray(items) && typeof items[0] === 'number') {
|
|
185
519
|
return items.reduce((s, x) => s + x, 0);
|
|
@@ -188,8 +522,8 @@ function sum(items) {
|
|
|
188
522
|
}
|
|
189
523
|
|
|
190
524
|
/**
|
|
191
|
-
*
|
|
192
|
-
*
|
|
525
|
+
* Calculates the visual state of an overlay scrollbar. All arguments are raw DOM numbers.
|
|
526
|
+
* Returns a state describing the visibility, size, and position of the thumb.
|
|
193
527
|
*/
|
|
194
528
|
function computeScrollbarState(scrollHeight = 0, clientHeight = 0, scrollTop = 0, minThumb = 24) {
|
|
195
529
|
const safeClient = toSafe(clientHeight);
|
|
@@ -217,14 +551,14 @@ function computeScrollbarState(scrollHeight = 0, clientHeight = 0, scrollTop = 0
|
|
|
217
551
|
maxScrollTop,
|
|
218
552
|
};
|
|
219
553
|
}
|
|
220
|
-
/**
|
|
554
|
+
/** Converts thumb position thumbTop (0..maxThumbTop) to scroll position scrollTop (0..maxScrollTop) */
|
|
221
555
|
function mapThumbTopToScrollTop(thumbTop, maxThumbTop, maxScrollTop) {
|
|
222
556
|
const mt = Math.max(1, toSafe(maxThumbTop));
|
|
223
557
|
const ms = Math.max(1, toSafe(maxScrollTop));
|
|
224
558
|
const tt = clamp(toSafe(thumbTop), 0, mt);
|
|
225
559
|
return (tt / mt) * ms;
|
|
226
560
|
}
|
|
227
|
-
/**
|
|
561
|
+
/** Clamps the new thumb position within the bounds 0..maxThumbTop */
|
|
228
562
|
function clampThumbTop(value, maxThumbTop) {
|
|
229
563
|
const mt = Math.max(1, toSafe(maxThumbTop));
|
|
230
564
|
return clamp(toSafe(value), 0, mt);
|
|
@@ -243,22 +577,68 @@ function toSafe(n) {
|
|
|
243
577
|
return Number.isFinite(n) ? n : 0;
|
|
244
578
|
}
|
|
245
579
|
function clamp(v, min, max) {
|
|
246
|
-
if (v < min)
|
|
580
|
+
if (v < min) {
|
|
247
581
|
return min;
|
|
248
|
-
|
|
582
|
+
}
|
|
583
|
+
if (v > max) {
|
|
249
584
|
return max;
|
|
585
|
+
}
|
|
250
586
|
return v;
|
|
251
587
|
}
|
|
252
588
|
|
|
589
|
+
/**
|
|
590
|
+
* Detects the primary scroll direction of an HTML element.
|
|
591
|
+
*
|
|
592
|
+
* Compares the current scroll position with the previous one to determine
|
|
593
|
+
* whether the user is scrolling horizontally, vertically, or not at all.
|
|
594
|
+
*
|
|
595
|
+
* Example:
|
|
596
|
+
* ```typescript
|
|
597
|
+
* const detector = new ScrollDirectionDetector(scrollableDiv);
|
|
598
|
+
*
|
|
599
|
+
* scrollableDiv.addEventListener('scroll', () => {
|
|
600
|
+
* const direction = detector.detect();
|
|
601
|
+
* console.log('Scroll direction:', direction); // 'horizontal' | 'vertical' | 'none'
|
|
602
|
+
* });
|
|
603
|
+
* ```
|
|
604
|
+
*
|
|
605
|
+
* The detector stores the previous scroll position internally and updates it
|
|
606
|
+
* on each `detect()` call to track movement deltas.
|
|
607
|
+
*/
|
|
253
608
|
class ScrollDirectionDetector {
|
|
254
609
|
el;
|
|
610
|
+
/**
|
|
611
|
+
* Previously recorded vertical scroll position (scrollTop).
|
|
612
|
+
* @private
|
|
613
|
+
*/
|
|
255
614
|
#prevTop = 0;
|
|
615
|
+
/**
|
|
616
|
+
* Previously recorded horizontal scroll position (scrollLeft).
|
|
617
|
+
* @private
|
|
618
|
+
*/
|
|
256
619
|
#prevLeft = 0;
|
|
620
|
+
/**
|
|
621
|
+
* Creates a new scroll direction detector for the given HTML element.
|
|
622
|
+
*
|
|
623
|
+
* @param el - The HTML element whose scroll direction will be tracked.
|
|
624
|
+
* The element must be scrollable (have overflow content).
|
|
625
|
+
*/
|
|
257
626
|
constructor(el) {
|
|
258
627
|
this.el = el;
|
|
259
628
|
this.#prevTop = el.scrollTop;
|
|
260
629
|
this.#prevLeft = el.scrollLeft;
|
|
261
630
|
}
|
|
631
|
+
/**
|
|
632
|
+
* Detects the primary scroll direction based on position changes since the last call.
|
|
633
|
+
*
|
|
634
|
+
* Compares the absolute deltas of horizontal and vertical scroll positions.
|
|
635
|
+
* The direction with the greater delta is considered the primary scroll direction.
|
|
636
|
+
*
|
|
637
|
+
* @returns The detected scroll direction:
|
|
638
|
+
* - `'horizontal'` if horizontal movement is greater
|
|
639
|
+
* - `'vertical'` if vertical movement is greater
|
|
640
|
+
* - `'none'` if no scroll movement occurred or deltas are equal
|
|
641
|
+
*/
|
|
262
642
|
detect() {
|
|
263
643
|
const { scrollTop, scrollLeft } = this.el;
|
|
264
644
|
const deltaX = Math.abs(scrollLeft - this.#prevLeft);
|
|
@@ -275,6 +655,26 @@ class ScrollDirectionDetector {
|
|
|
275
655
|
}
|
|
276
656
|
}
|
|
277
657
|
|
|
658
|
+
/**
|
|
659
|
+
* Splits an array of grid columns into sticky left, sticky right, and all visible columns.
|
|
660
|
+
*
|
|
661
|
+
* This function filters columns by visibility, then divides them at the midpoint
|
|
662
|
+
* to identify which sticky columns should be pinned to the left versus right side
|
|
663
|
+
* of the data grid.
|
|
664
|
+
*
|
|
665
|
+
* @template Data - The data type extending `AnyDict` that the grid columns are based on.
|
|
666
|
+
* @param cols - Array of grid columns to be split.
|
|
667
|
+
* @returns An object containing:
|
|
668
|
+
* - `left`: Array of sticky columns from the first half of visible columns.
|
|
669
|
+
* - `right`: Array of sticky columns from the second half of visible columns.
|
|
670
|
+
* - `visible`: Array of all visible columns (filtered by `visible !== false`).
|
|
671
|
+
*
|
|
672
|
+
* @example
|
|
673
|
+
* ```typescript
|
|
674
|
+
* const columns: GridColumn<User>[] = [...];
|
|
675
|
+
* const { left, right, visible } = splitSticky(columns);
|
|
676
|
+
* ```
|
|
677
|
+
*/
|
|
278
678
|
function splitSticky(cols) {
|
|
279
679
|
const visible = cols.filter((c) => c.visible !== false);
|
|
280
680
|
const middleIndex = Math.floor(visible.length / 2);
|
|
@@ -283,20 +683,93 @@ function splitSticky(cols) {
|
|
|
283
683
|
return { left, right, visible };
|
|
284
684
|
}
|
|
285
685
|
|
|
686
|
+
/**
|
|
687
|
+
* View model for the data grid component.
|
|
688
|
+
*
|
|
689
|
+
* Manages grid state including column layout, sticky positioning, scrollbar calculations,
|
|
690
|
+
* and pinned rows. Automatically recomputes column widths based on container size and
|
|
691
|
+
* handles sticky column offsets for left and right pinned columns.
|
|
692
|
+
*
|
|
693
|
+
* @template Data - Type of data objects in the grid, must extend AnyDict
|
|
694
|
+
*
|
|
695
|
+
* @example
|
|
696
|
+
* ```typescript
|
|
697
|
+
* const gridVm = inject(DataGridVm<MyDataType>);
|
|
698
|
+
* gridVm.columns.set([{ key: 'name', title: 'Name' }]);
|
|
699
|
+
* gridVm.containerWidth.set(800);
|
|
700
|
+
* ```
|
|
701
|
+
*/
|
|
286
702
|
class DataGridVm {
|
|
703
|
+
/**
|
|
704
|
+
* Reference to the scrollable container element.
|
|
705
|
+
*
|
|
706
|
+
* Used for scrollbar calculations and scroll position management.
|
|
707
|
+
*/
|
|
287
708
|
scrollEl = signal(undefined, ...(ngDevMode ? [{ debugName: "scrollEl" }] : []));
|
|
709
|
+
/**
|
|
710
|
+
* Array of column configurations for the grid.
|
|
711
|
+
*
|
|
712
|
+
* Defines all columns including their keys, titles, sticky positioning, and widths.
|
|
713
|
+
* Value is reactive and triggers column layout recalculation when changed.
|
|
714
|
+
*/
|
|
288
715
|
columns = signal([], ...(ngDevMode ? [{ debugName: "columns" }] : []));
|
|
716
|
+
/**
|
|
717
|
+
* Array of pinned row configurations.
|
|
718
|
+
*
|
|
719
|
+
* Defines rows that remain fixed at the top or bottom of the grid during scrolling.
|
|
720
|
+
* Each row includes data, position ('top' or 'bottom'), and optional ordering.
|
|
721
|
+
*/
|
|
289
722
|
pinnedRows = signal([], ...(ngDevMode ? [{ debugName: "pinnedRows" }] : []));
|
|
723
|
+
/**
|
|
724
|
+
* Current width of the grid container in pixels.
|
|
725
|
+
*
|
|
726
|
+
* Used for column width calculations and layout adjustments.
|
|
727
|
+
* Value is reactive and triggers column layout recalculation when changed.
|
|
728
|
+
*/
|
|
290
729
|
containerWidth = signal(0, ...(ngDevMode ? [{ debugName: "containerWidth" }] : []));
|
|
730
|
+
/**
|
|
731
|
+
* Flag indicating whether the custom scrollbar should be visible.
|
|
732
|
+
*
|
|
733
|
+
* Automatically computed based on content height vs. container height.
|
|
734
|
+
*/
|
|
291
735
|
scrollbarVisible = signal(false, ...(ngDevMode ? [{ debugName: "scrollbarVisible" }] : []));
|
|
736
|
+
/**
|
|
737
|
+
* Height of the scrollbar thumb in pixels.
|
|
738
|
+
*
|
|
739
|
+
* Proportional to the ratio of visible content to total content height.
|
|
740
|
+
*/
|
|
292
741
|
thumbHeightPx = signal(0, ...(ngDevMode ? [{ debugName: "thumbHeightPx" }] : []));
|
|
742
|
+
/**
|
|
743
|
+
* Top position of the scrollbar thumb in pixels.
|
|
744
|
+
*
|
|
745
|
+
* Corresponds to the current scroll position within the scrollable area.
|
|
746
|
+
*/
|
|
293
747
|
thumbTopPx = signal(0, ...(ngDevMode ? [{ debugName: "thumbTopPx" }] : []));
|
|
748
|
+
/**
|
|
749
|
+
* Flag indicating whether the user is currently dragging the scrollbar thumb.
|
|
750
|
+
*
|
|
751
|
+
* Used to track active scroll drag interactions.
|
|
752
|
+
*/
|
|
294
753
|
dragging = false;
|
|
754
|
+
/**
|
|
755
|
+
* Map of global cell renderer templates by type.
|
|
756
|
+
*
|
|
757
|
+
* Stores reusable template references for different cell renderer types
|
|
758
|
+
* that can be shared across multiple columns.
|
|
759
|
+
*/
|
|
295
760
|
globalTypeCellTpls = new Map();
|
|
761
|
+
globalDataCellTpls = new Map();
|
|
296
762
|
#byKey = signal({}, ...(ngDevMode ? [{ debugName: "#byKey" }] : []));
|
|
297
763
|
#defaultColWidth = 140;
|
|
298
764
|
#stickyLeftMap = new Map();
|
|
299
765
|
#stickyRightMap = new Map();
|
|
766
|
+
/**
|
|
767
|
+
* Computed an array of non-sticky columns to display in the scrollable area.
|
|
768
|
+
*
|
|
769
|
+
* Automatically splits columns into left-sticky, visible, and right-sticky groups,
|
|
770
|
+
* recomputes sticky offsets, and returns only the scrollable middle section.
|
|
771
|
+
* Recalculates whenever columns or container width changes.
|
|
772
|
+
*/
|
|
300
773
|
columnsToShow = computed(() => {
|
|
301
774
|
this.containerWidth();
|
|
302
775
|
const cols = this.columns() ?? [];
|
|
@@ -304,7 +777,19 @@ class DataGridVm {
|
|
|
304
777
|
this.recomputeStickyOffsets(left, right);
|
|
305
778
|
return visible;
|
|
306
779
|
}, ...(ngDevMode ? [{ debugName: "columnsToShow" }] : []));
|
|
780
|
+
/**
|
|
781
|
+
* Computed array of rows pinned to the top of the grid.
|
|
782
|
+
*
|
|
783
|
+
* Filters pinned rows by 'top' position and sorts them by order property.
|
|
784
|
+
* Rows with lower order values appear first.
|
|
785
|
+
*/
|
|
307
786
|
pinnedTop = computed(() => (this.pinnedRows() ?? []).filter((r) => r.position === 'top').sort((a, b) => (a.order ?? 0) - (b.order ?? 0)), ...(ngDevMode ? [{ debugName: "pinnedTop" }] : []));
|
|
787
|
+
/**
|
|
788
|
+
* Computed array of rows pinned to the bottom of the grid.
|
|
789
|
+
*
|
|
790
|
+
* Filters pinned rows by 'bottom' position and sorts them by order property.
|
|
791
|
+
* Rows with lower order values appear first.
|
|
792
|
+
*/
|
|
308
793
|
pinnedBottom = computed(() => (this.pinnedRows() ?? []).filter((r) => r.position === 'bottom').sort((a, b) => (a.order ?? 0) - (b.order ?? 0)), ...(ngDevMode ? [{ debugName: "pinnedBottom" }] : []));
|
|
309
794
|
constructor() {
|
|
310
795
|
effect(() => {
|
|
@@ -317,14 +802,51 @@ class DataGridVm {
|
|
|
317
802
|
this.#byKey.set(res.byKey);
|
|
318
803
|
});
|
|
319
804
|
}
|
|
805
|
+
/**
|
|
806
|
+
* Returns the computed width for a column by its key.
|
|
807
|
+
*
|
|
808
|
+
* If the column width has not been calculated, returns the default column width.
|
|
809
|
+
*
|
|
810
|
+
* @param key - The unique identifier for the column
|
|
811
|
+
* @returns Width in pixels
|
|
812
|
+
*/
|
|
320
813
|
widthByKey = (key) => this.#byKey()[key] ?? this.#defaultColWidth;
|
|
814
|
+
/**
|
|
815
|
+
* Checks if a column is pinned to the left side of the grid.
|
|
816
|
+
*
|
|
817
|
+
* @param key - The unique identifier for the column
|
|
818
|
+
* @returns `true` if the column is sticky on the left, `false` otherwise
|
|
819
|
+
*/
|
|
321
820
|
isStickyLeft = (key) => this.#stickyLeftMap.has(key);
|
|
821
|
+
/**
|
|
822
|
+
* Checks if a column is pinned to the right side of the grid.
|
|
823
|
+
*
|
|
824
|
+
* @param key - The unique identifier for the column
|
|
825
|
+
* @returns `true` if the column is sticky on the right, `false` otherwise
|
|
826
|
+
*/
|
|
322
827
|
isStickyRight = (key) => this.#stickyRightMap.has(key);
|
|
828
|
+
/**
|
|
829
|
+
* Returns the horizontal offset for a sticky column.
|
|
830
|
+
*
|
|
831
|
+
* Calculates the distance from the specified edge (left or right) where the column
|
|
832
|
+
* should be positioned. Returns `null` if the column is not sticky in the given direction.
|
|
833
|
+
*
|
|
834
|
+
* @param key - The unique identifier for the column
|
|
835
|
+
* @param dir - The direction to check ('left' or 'right')
|
|
836
|
+
* @returns Offset in pixels, or `null` if not sticky in the specified direction
|
|
837
|
+
*/
|
|
323
838
|
stickyOffset = (key, dir) => dir === 'left' && this.isStickyLeft(key)
|
|
324
839
|
? (this.#stickyLeftMap.get(key) ?? null)
|
|
325
840
|
: dir === 'right' && this.isStickyRight(key)
|
|
326
841
|
? (this.#stickyRightMap.get(key) ?? null)
|
|
327
842
|
: null;
|
|
843
|
+
/**
|
|
844
|
+
* Calculates and updates scrollbar state based on the current scroll position.
|
|
845
|
+
*
|
|
846
|
+
* Computes whether the scrollbar should be visible, and if so, determines
|
|
847
|
+
* the thumb height and position based on scroll height, client height, and scroll position.
|
|
848
|
+
* Updates the corresponding signal properties with the calculated values.
|
|
849
|
+
*/
|
|
328
850
|
calcScrollbar() {
|
|
329
851
|
const el = this.scrollEl()?.nativeElement;
|
|
330
852
|
if (!el)
|
|
@@ -354,18 +876,77 @@ class DataGridVm {
|
|
|
354
876
|
acc += this.widthByKey(col.key);
|
|
355
877
|
}
|
|
356
878
|
}
|
|
357
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.
|
|
358
|
-
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.
|
|
879
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.1", ngImport: i0, type: DataGridVm, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
880
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.1.1", ngImport: i0, type: DataGridVm });
|
|
359
881
|
}
|
|
360
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.
|
|
882
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.1", ngImport: i0, type: DataGridVm, decorators: [{
|
|
361
883
|
type: Injectable
|
|
362
884
|
}], ctorParameters: () => [] });
|
|
363
885
|
|
|
886
|
+
/**
|
|
887
|
+
* Component for rendering individual data grid cells.
|
|
888
|
+
*
|
|
889
|
+
* Handles different cell rendering strategies based on column configuration:
|
|
890
|
+
* - Custom templates via `renderTemplate`
|
|
891
|
+
* - Global type-specific templates registered in DataGridVm
|
|
892
|
+
* - Built-in formatters for dates and numbers
|
|
893
|
+
* - Index display (1-based row numbering)
|
|
894
|
+
* - Plain text for simple values
|
|
895
|
+
*
|
|
896
|
+
* The component automatically determines the appropriate rendering method
|
|
897
|
+
* based on column properties and applies Angular pipes when needed.
|
|
898
|
+
*
|
|
899
|
+
* @template Data - Type of data objects in the grid row
|
|
900
|
+
*
|
|
901
|
+
* @example
|
|
902
|
+
* ```html
|
|
903
|
+
* <re-data-grid-cell
|
|
904
|
+
* [index]="0"
|
|
905
|
+
* [item]="rowData"
|
|
906
|
+
* [column]="columnConfig"
|
|
907
|
+
* />
|
|
908
|
+
* ```
|
|
909
|
+
*/
|
|
364
910
|
class DataGridCellComponent {
|
|
911
|
+
/**
|
|
912
|
+
* Zero-based index of the current row in the data grid.
|
|
913
|
+
*
|
|
914
|
+
* Used for rendering row numbers (displayed as 1-based in 'index' type columns)
|
|
915
|
+
* and passed to custom templates as context.
|
|
916
|
+
*/
|
|
365
917
|
index = input.required(...(ngDevMode ? [{ debugName: "index" }] : []));
|
|
918
|
+
/**
|
|
919
|
+
* Data object for the current row.
|
|
920
|
+
*
|
|
921
|
+
* Contains the complete row data that can be accessed by column value functions
|
|
922
|
+
* or custom render templates. Type-safe according to the Data generic parameter.
|
|
923
|
+
*/
|
|
366
924
|
item = input.required(...(ngDevMode ? [{ debugName: "item" }] : []));
|
|
925
|
+
/**
|
|
926
|
+
* Column configuration object.
|
|
927
|
+
*
|
|
928
|
+
* Defines how the cell should be rendered, including key mapping, type,
|
|
929
|
+
* custom templates, or value transformation functions.
|
|
930
|
+
*/
|
|
367
931
|
column = input.required(...(ngDevMode ? [{ debugName: "column" }] : []));
|
|
932
|
+
/**
|
|
933
|
+
* Injected DataGridVm service instance.
|
|
934
|
+
*
|
|
935
|
+
* Provides access to global type-specific cell templates registered
|
|
936
|
+
* at the grid level via `globalTypeCellTpls` map.
|
|
937
|
+
*/
|
|
368
938
|
vm = inject(DataGridVm);
|
|
939
|
+
/**
|
|
940
|
+
* Computed rendering strategy for the current cell.
|
|
941
|
+
*
|
|
942
|
+
* Determines which template block to use based on column configuration:
|
|
943
|
+
* - `'tpl'` - Custom column template (`renderTemplate` property exists)
|
|
944
|
+
* - `'globalTypeTpl'` - Global type template (registered in DataGridVm)
|
|
945
|
+
* - `'date'`, `'number'`, `'index'` - Built-in formatters
|
|
946
|
+
* - `'plain'` - Default text rendering
|
|
947
|
+
*
|
|
948
|
+
* The value is reactive and updates when column configuration changes.
|
|
949
|
+
*/
|
|
369
950
|
type = computed(() => {
|
|
370
951
|
const col = this.column();
|
|
371
952
|
if ('renderTemplate' in col) {
|
|
@@ -374,15 +955,28 @@ class DataGridCellComponent {
|
|
|
374
955
|
if ('type' in col && this.vm.globalTypeCellTpls.has(col.type)) {
|
|
375
956
|
return 'globalTypeTpl';
|
|
376
957
|
}
|
|
958
|
+
if (this.vm.globalDataCellTpls.has(col.key)) {
|
|
959
|
+
return 'globalDataTpl';
|
|
960
|
+
}
|
|
377
961
|
return 'type' in col ? col['type'] : 'plain';
|
|
378
962
|
}, ...(ngDevMode ? [{ debugName: "type" }] : []));
|
|
963
|
+
/**
|
|
964
|
+
* Computed cell value extracted from row data.
|
|
965
|
+
*
|
|
966
|
+
* Returns the cell's display value by either:
|
|
967
|
+
* - Calling the `value` function from column configuration (if defined)
|
|
968
|
+
* - Directly accessing the row property using the column's `key`
|
|
969
|
+
*
|
|
970
|
+
* The value is reactive and updates when row data or column configuration changes.
|
|
971
|
+
* Used as input for templates, pipes, and default text rendering.
|
|
972
|
+
*/
|
|
379
973
|
value = computed(() => {
|
|
380
974
|
const col = this.column();
|
|
381
975
|
const row = this.item();
|
|
382
976
|
return 'value' in col ? col.value(row) : row[col.key];
|
|
383
977
|
}, ...(ngDevMode ? [{ debugName: "value" }] : []));
|
|
384
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.
|
|
385
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.
|
|
978
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.1", ngImport: i0, type: DataGridCellComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
979
|
+
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: `
|
|
386
980
|
@let row = item();
|
|
387
981
|
@let col = column();
|
|
388
982
|
@let val = value();
|
|
@@ -400,11 +994,17 @@ class DataGridCellComponent {
|
|
|
400
994
|
[ngTemplateOutletContext]="{ $implicit: val, value: val, row, col }"
|
|
401
995
|
/>
|
|
402
996
|
}
|
|
997
|
+
@case ('globalDataTpl') {
|
|
998
|
+
<ng-container
|
|
999
|
+
[ngTemplateOutlet]="vm.globalDataCellTpls.get($any(col).key)"
|
|
1000
|
+
[ngTemplateOutletContext]="{ $implicit: val, value: val, row, col }"
|
|
1001
|
+
/>
|
|
1002
|
+
}
|
|
403
1003
|
@case ('date') {
|
|
404
|
-
{{ val | date }}
|
|
1004
|
+
{{ val | date: $any(col)?.typeParams }}
|
|
405
1005
|
}
|
|
406
1006
|
@case ('number') {
|
|
407
|
-
{{ val | number }}
|
|
1007
|
+
{{ val | number: $any(col)?.typeParams }}
|
|
408
1008
|
}
|
|
409
1009
|
@case ('index') {
|
|
410
1010
|
{{ index() + 1 }}
|
|
@@ -415,7 +1015,7 @@ class DataGridCellComponent {
|
|
|
415
1015
|
}
|
|
416
1016
|
`, 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" }] });
|
|
417
1017
|
}
|
|
418
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.
|
|
1018
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.1", ngImport: i0, type: DataGridCellComponent, decorators: [{
|
|
419
1019
|
type: Component,
|
|
420
1020
|
args: [{ selector: 're-data-grid-cell', template: `
|
|
421
1021
|
@let row = item();
|
|
@@ -435,11 +1035,17 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.3", ngImpor
|
|
|
435
1035
|
[ngTemplateOutletContext]="{ $implicit: val, value: val, row, col }"
|
|
436
1036
|
/>
|
|
437
1037
|
}
|
|
1038
|
+
@case ('globalDataTpl') {
|
|
1039
|
+
<ng-container
|
|
1040
|
+
[ngTemplateOutlet]="vm.globalDataCellTpls.get($any(col).key)"
|
|
1041
|
+
[ngTemplateOutletContext]="{ $implicit: val, value: val, row, col }"
|
|
1042
|
+
/>
|
|
1043
|
+
}
|
|
438
1044
|
@case ('date') {
|
|
439
|
-
{{ val | date }}
|
|
1045
|
+
{{ val | date: $any(col)?.typeParams }}
|
|
440
1046
|
}
|
|
441
1047
|
@case ('number') {
|
|
442
|
-
{{ val | number }}
|
|
1048
|
+
{{ val | number: $any(col)?.typeParams }}
|
|
443
1049
|
}
|
|
444
1050
|
@case ('index') {
|
|
445
1051
|
{{ index() + 1 }}
|
|
@@ -451,19 +1057,87 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.3", ngImpor
|
|
|
451
1057
|
`, imports: [NgTemplateOutlet, DatePipe, DecimalPipe], styles: [":host{width:100%;text-align:inherit}\n"] }]
|
|
452
1058
|
}], 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 }] }] } });
|
|
453
1059
|
|
|
1060
|
+
/**
|
|
1061
|
+
* Service class for managing row selection in a data grid.
|
|
1062
|
+
*
|
|
1063
|
+
* Handles selection state and operations for grid rows, supporting multiple selection modes
|
|
1064
|
+
* (none, single, multi). Provides reactive signals for tracking selected items and
|
|
1065
|
+
* computed values for selection state.
|
|
1066
|
+
*
|
|
1067
|
+
* @template Data - The type of data objects in the grid, must extend `AnyDict`.
|
|
1068
|
+
*
|
|
1069
|
+
* @example
|
|
1070
|
+
* ```typescript
|
|
1071
|
+
* const selector = new Selector<User>();
|
|
1072
|
+
* selector.data.set(users);
|
|
1073
|
+
* selector.selection.set({ mode: 'multi', key: 'id' });
|
|
1074
|
+
* selector.select(users[0]);
|
|
1075
|
+
* console.log(selector.selectedKeys()); // ['user-id']
|
|
1076
|
+
* ```
|
|
1077
|
+
*/
|
|
454
1078
|
class Selector {
|
|
1079
|
+
/**
|
|
1080
|
+
* Signal containing the full dataset of grid rows.
|
|
1081
|
+
*
|
|
1082
|
+
* This signal holds all data items that can be selected.
|
|
1083
|
+
* Defaults to an empty array.
|
|
1084
|
+
*/
|
|
455
1085
|
data = signal([], ...(ngDevMode ? [{ debugName: "data" }] : []));
|
|
1086
|
+
/**
|
|
1087
|
+
* Signal containing the current selection configuration.
|
|
1088
|
+
*
|
|
1089
|
+
* Defines the selection mode and the key property used for identifying rows.
|
|
1090
|
+
* Defaults to `{ mode: 'none' }` which disables selection.
|
|
1091
|
+
*/
|
|
456
1092
|
selection = signal({ mode: 'none' }, ...(ngDevMode ? [{ debugName: "selection" }] : []));
|
|
1093
|
+
/**
|
|
1094
|
+
* Signal containing the array of currently selected row keys.
|
|
1095
|
+
*
|
|
1096
|
+
* Stores the keys of all selected rows based on the key property
|
|
1097
|
+
* defined in the selection configuration.
|
|
1098
|
+
* Defaults to an empty array.
|
|
1099
|
+
*/
|
|
457
1100
|
selectedKeys = signal([], ...(ngDevMode ? [{ debugName: "selectedKeys" }] : []));
|
|
1101
|
+
/**
|
|
1102
|
+
* Computed signal indicating the overall selection state of all rows.
|
|
1103
|
+
*
|
|
1104
|
+
* Returns:
|
|
1105
|
+
* - `true` if all rows are selected
|
|
1106
|
+
* - `false` if no rows are selected
|
|
1107
|
+
* - `'mixed'` if some but not all rows are selected
|
|
1108
|
+
*
|
|
1109
|
+
* Useful for implementing "select all" checkbox with indeterminate state.
|
|
1110
|
+
*/
|
|
458
1111
|
isAllSelected = computed(() => {
|
|
459
1112
|
const selectedCount = this.selectedKeys().length;
|
|
460
1113
|
return selectedCount === this.data().length ? true : selectedCount === 0 ? false : 'mixed';
|
|
461
1114
|
}, ...(ngDevMode ? [{ debugName: "isAllSelected" }] : []));
|
|
1115
|
+
/**
|
|
1116
|
+
* Checks whether a specific row is currently selected.
|
|
1117
|
+
*
|
|
1118
|
+
* Compares the row's key value against the list of selected keys.
|
|
1119
|
+
* Returns `false` if selection mode is 'none' or key is not configured.
|
|
1120
|
+
*
|
|
1121
|
+
* @param row - The data row to check selection status for.
|
|
1122
|
+
* @returns `true` if the row is selected, `false` otherwise.
|
|
1123
|
+
*/
|
|
462
1124
|
isSelected(row) {
|
|
463
1125
|
const selection = this.selection();
|
|
464
1126
|
const selected = this.selectedKeys();
|
|
465
1127
|
return 'key' in selection ? selected.includes(row[selection.key]) : false;
|
|
466
1128
|
}
|
|
1129
|
+
/**
|
|
1130
|
+
* Toggles selection of all rows.
|
|
1131
|
+
*
|
|
1132
|
+
* If all rows are selected, deselects all.
|
|
1133
|
+
* If no rows or some rows are selected, selects all.
|
|
1134
|
+
*
|
|
1135
|
+
* This method only works in `'multi'` selection mode and throws an error
|
|
1136
|
+
* if called in any other mode.
|
|
1137
|
+
*
|
|
1138
|
+
* @returns The updated array of selected keys.
|
|
1139
|
+
* @throws {Error} If selection mode is not `'multi'`.
|
|
1140
|
+
*/
|
|
467
1141
|
selectAll() {
|
|
468
1142
|
if (this.selection().mode !== 'multi') {
|
|
469
1143
|
throw new Error('Cannot select all in not "multi" mode');
|
|
@@ -474,6 +1148,18 @@ class Selector {
|
|
|
474
1148
|
this.selectedKeys.set(!nextState ? [] : this.data().map((row) => row[selection.key]));
|
|
475
1149
|
return this.selectedKeys();
|
|
476
1150
|
}
|
|
1151
|
+
/**
|
|
1152
|
+
* Selects or deselects a specific row.
|
|
1153
|
+
*
|
|
1154
|
+
* Behavior depends on the selection mode:
|
|
1155
|
+
* - In `'single'` mode: replaces current selection with the specified row.
|
|
1156
|
+
* - In `'multi'` mode: toggles the row's selection state (adds if not selected, removes if selected).
|
|
1157
|
+
* - In `'none'` mode: throws an error.
|
|
1158
|
+
*
|
|
1159
|
+
* @param row - The data row to select or deselect.
|
|
1160
|
+
* @returns The updated array of selected keys after the operation.
|
|
1161
|
+
* @throws {Error} If selection mode is `'none'`.
|
|
1162
|
+
*/
|
|
477
1163
|
select(row) {
|
|
478
1164
|
if (this.selection().mode === 'none') {
|
|
479
1165
|
throw new Error('Cannot select row in "none" mode');
|
|
@@ -525,8 +1211,8 @@ class CheckboxIcon {
|
|
|
525
1211
|
}
|
|
526
1212
|
});
|
|
527
1213
|
}
|
|
528
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.
|
|
529
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.2.0", version: "21.
|
|
1214
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.1", ngImport: i0, type: CheckboxIcon, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
1215
|
+
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: `
|
|
530
1216
|
<label class="cb" [class.cb--disabled]="disabled()">
|
|
531
1217
|
<input
|
|
532
1218
|
#box
|
|
@@ -542,7 +1228,7 @@ class CheckboxIcon {
|
|
|
542
1228
|
</label>
|
|
543
1229
|
`, 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 });
|
|
544
1230
|
}
|
|
545
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.
|
|
1231
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.1", ngImport: i0, type: CheckboxIcon, decorators: [{
|
|
546
1232
|
type: Component,
|
|
547
1233
|
args: [{ selector: 're-checkbox-ic', standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, template: `
|
|
548
1234
|
<label class="cb" [class.cb--disabled]="disabled()">
|
|
@@ -564,8 +1250,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.3", ngImpor
|
|
|
564
1250
|
/* eslint-disable max-len */
|
|
565
1251
|
class ExpandIcon {
|
|
566
1252
|
expanded = input(false, { ...(ngDevMode ? { debugName: "expanded" } : {}), transform: booleanAttribute });
|
|
567
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.
|
|
568
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.
|
|
1253
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.1", ngImport: i0, type: ExpandIcon, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
1254
|
+
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: `
|
|
569
1255
|
@if (expanded()) {
|
|
570
1256
|
<svg width="16" height="16" viewBox="0 0 16 16">
|
|
571
1257
|
<path
|
|
@@ -583,7 +1269,7 @@ class ExpandIcon {
|
|
|
583
1269
|
}
|
|
584
1270
|
`, isInline: true, styles: [""] });
|
|
585
1271
|
}
|
|
586
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.
|
|
1272
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.1", ngImport: i0, type: ExpandIcon, decorators: [{
|
|
587
1273
|
type: Component,
|
|
588
1274
|
args: [{ selector: 're-expand-ic', template: `
|
|
589
1275
|
@if (expanded()) {
|
|
@@ -607,8 +1293,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.3", ngImpor
|
|
|
607
1293
|
/* eslint-disable max-len */
|
|
608
1294
|
class SortIcon {
|
|
609
1295
|
direction = input('asc', ...(ngDevMode ? [{ debugName: "direction" }] : []));
|
|
610
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.
|
|
611
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "21.
|
|
1296
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.1", ngImport: i0, type: SortIcon, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
1297
|
+
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: `
|
|
612
1298
|
<svg width="18" height="18" viewBox="0 0 18 18" class="{{ direction() }}">
|
|
613
1299
|
<path
|
|
614
1300
|
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"
|
|
@@ -617,7 +1303,7 @@ class SortIcon {
|
|
|
617
1303
|
</svg>
|
|
618
1304
|
`, 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"] });
|
|
619
1305
|
}
|
|
620
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.
|
|
1306
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.1", ngImport: i0, type: SortIcon, decorators: [{
|
|
621
1307
|
type: Component,
|
|
622
1308
|
args: [{ selector: 're-sort-ic', template: `
|
|
623
1309
|
<svg width="18" height="18" viewBox="0 0 18 18" class="{{ direction() }}">
|
|
@@ -629,57 +1315,192 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.3", ngImpor
|
|
|
629
1315
|
`, styles: ["svg{transition:transform .3s ease-in-out}svg:not(.asc):not(.desc){display:none}.asc{transform:rotate(-180deg)}.desc{transform:rotate(0)}\n"] }]
|
|
630
1316
|
}], propDecorators: { direction: [{ type: i0.Input, args: [{ isSignal: true, alias: "direction", required: false }] }] } });
|
|
631
1317
|
|
|
1318
|
+
/**
|
|
1319
|
+
* Data grid component with virtual scrolling, sorting, selection, and pagination support.
|
|
1320
|
+
*
|
|
1321
|
+
* Provides high-performance rendering of large datasets with features like:
|
|
1322
|
+
* - Virtual scrolling for efficient DOM management
|
|
1323
|
+
* - Column sorting and expandable columns
|
|
1324
|
+
* - Row selection (single/multiple)
|
|
1325
|
+
* - Pagination modes: none, pagination, or infinite scroll
|
|
1326
|
+
* - Pinned rows
|
|
1327
|
+
* - Custom cell and header templates
|
|
1328
|
+
*
|
|
1329
|
+
* @example
|
|
1330
|
+
* ```html
|
|
1331
|
+
* <re-data-grid
|
|
1332
|
+
* [data]="users"
|
|
1333
|
+
* [columns]="columns"
|
|
1334
|
+
* [selection]="{ mode: 'multiple' }"
|
|
1335
|
+
* (sortChange)="onSort($event)"
|
|
1336
|
+
* />
|
|
1337
|
+
* ```
|
|
1338
|
+
*/
|
|
632
1339
|
class DataGrid {
|
|
633
|
-
/**
|
|
1340
|
+
/**
|
|
1341
|
+
* Array of data to display in the table.
|
|
1342
|
+
*
|
|
1343
|
+
* Each item represents a single row. The component will efficiently render
|
|
1344
|
+
* only visible rows using virtual scrolling.
|
|
1345
|
+
*/
|
|
634
1346
|
data = input([], ...(ngDevMode ? [{ debugName: "data" }] : []));
|
|
635
|
-
/**
|
|
1347
|
+
/**
|
|
1348
|
+
* Column configuration for the table.
|
|
1349
|
+
*
|
|
1350
|
+
* Defines how each column should be rendered, sorted, and styled.
|
|
1351
|
+
* Supports custom templates, sorting keys, and expandable columns.
|
|
1352
|
+
*/
|
|
636
1353
|
columns = input([], ...(ngDevMode ? [{ debugName: "columns" }] : []));
|
|
637
|
-
/**
|
|
1354
|
+
/**
|
|
1355
|
+
* Pagination mode: 'none', 'pagination', or 'infinity'.
|
|
1356
|
+
*
|
|
1357
|
+
* - `none` - No pagination, all data is rendered
|
|
1358
|
+
* - `pagination` - Classic page-based pagination with fixed page size
|
|
1359
|
+
* - `infinity` - Infinite scroll mode, loads more data as user scrolls
|
|
1360
|
+
*/
|
|
638
1361
|
mode = input('none', ...(ngDevMode ? [{ debugName: "mode" }] : []));
|
|
639
|
-
/**
|
|
1362
|
+
/**
|
|
1363
|
+
* Array of pinned rows that remain visible at the top or bottom.
|
|
1364
|
+
*
|
|
1365
|
+
* Pinned rows stay fixed while the rest of the content scrolls.
|
|
1366
|
+
* Useful for totals, summaries, or always-visible items.
|
|
1367
|
+
*/
|
|
640
1368
|
pinnedRows = input([], ...(ngDevMode ? [{ debugName: "pinnedRows" }] : []));
|
|
641
|
-
/**
|
|
1369
|
+
/**
|
|
1370
|
+
* Whether to add an index column showing row numbers.
|
|
1371
|
+
*
|
|
1372
|
+
* When enabled, it automatically adds a column displaying sequential row numbers.
|
|
1373
|
+
*/
|
|
642
1374
|
hasIndexColumn = input(false, { ...(ngDevMode ? { debugName: "hasIndexColumn" } : {}), transform: booleanAttribute });
|
|
643
|
-
/**
|
|
1375
|
+
/**
|
|
1376
|
+
* Row selection configuration.
|
|
1377
|
+
*
|
|
1378
|
+
* Controls whether users can select rows and in what mode:
|
|
1379
|
+
* - `{ mode: 'none' }` - No selection
|
|
1380
|
+
* - `{ mode: 'single' }` - Single row selection
|
|
1381
|
+
* - `{ mode: 'multi' }` - Multiple row selection with checkboxes
|
|
1382
|
+
*
|
|
1383
|
+
* When selection is enabled, a `key` must be provided to identify rows.
|
|
1384
|
+
*/
|
|
644
1385
|
selection = input({ mode: 'none' }, ...(ngDevMode ? [{ debugName: "selection" }] : []));
|
|
645
|
-
/**
|
|
1386
|
+
/**
|
|
1387
|
+
* Number of items per page.
|
|
1388
|
+
*
|
|
1389
|
+
* Used in pagination and infinity scroll modes to control
|
|
1390
|
+
* how many rows are loaded at once. Default is 20.
|
|
1391
|
+
*/
|
|
646
1392
|
pageSize = input(20, { ...(ngDevMode ? { debugName: "pageSize" } : {}), transform: numberAttribute });
|
|
647
|
-
/**
|
|
1393
|
+
/**
|
|
1394
|
+
* Height of each row in pixels.
|
|
1395
|
+
*
|
|
1396
|
+
* Used for virtual scrolling calculations. Must be consistent
|
|
1397
|
+
* across all rows for accurate scrolling. Default is 40.
|
|
1398
|
+
*/
|
|
648
1399
|
rowHeight = input(40, { ...(ngDevMode ? { debugName: "rowHeight" } : {}), transform: numberAttribute });
|
|
649
|
-
/**
|
|
650
|
-
|
|
651
|
-
|
|
1400
|
+
/**
|
|
1401
|
+
* Grid height configuration.
|
|
1402
|
+
*
|
|
1403
|
+
* - `number` - Fixed height in pixels
|
|
1404
|
+
* - `'full'` - Fill container height (100%), can be managed by host component style
|
|
1405
|
+
* - `'default'` - Height by CSS var (--re-data-grid-height)
|
|
1406
|
+
*/
|
|
1407
|
+
height = input('default', ...(ngDevMode ? [{ debugName: "height" }] : []));
|
|
1408
|
+
/**
|
|
1409
|
+
* Size of the virtual scroll buffer.
|
|
1410
|
+
*
|
|
1411
|
+
* Number of extra rows to render above and below the viewport
|
|
1412
|
+
* to reduce flickering during fast scrolling. Default is 6.
|
|
1413
|
+
*/
|
|
652
1414
|
virtualBuffer = input(6, ...(ngDevMode ? [{ debugName: "virtualBuffer" }] : []));
|
|
653
|
-
/**
|
|
1415
|
+
/**
|
|
1416
|
+
* Loading state indicator.
|
|
1417
|
+
*
|
|
1418
|
+
* When true, displays loading template instead of data.
|
|
1419
|
+
* Useful during async data fetching operations.
|
|
1420
|
+
*/
|
|
654
1421
|
loading = input(false, { ...(ngDevMode ? { debugName: "loading" } : {}), transform: booleanAttribute });
|
|
655
|
-
/**
|
|
1422
|
+
/**
|
|
1423
|
+
* Function or property name for obtaining a unique row key.
|
|
1424
|
+
*
|
|
1425
|
+
* Used for efficient change detection and row tracking.
|
|
1426
|
+
* Can be a property name (string) or a function that returns a unique identifier.
|
|
1427
|
+
*/
|
|
656
1428
|
rowKey = input(undefined, ...(ngDevMode ? [{ debugName: "rowKey" }] : []));
|
|
657
|
-
/**
|
|
1429
|
+
/**
|
|
1430
|
+
* Whether to start a page count from 0 (true) or 1 (false).
|
|
1431
|
+
*
|
|
1432
|
+
* Controls the numbering scheme for pagination events.
|
|
1433
|
+
* Default is true (0-based indexing).
|
|
1434
|
+
*/
|
|
658
1435
|
pageStartFromZero = input(true, { ...(ngDevMode ? { debugName: "pageStartFromZero" } : {}), transform: booleanAttribute });
|
|
659
|
-
/**
|
|
1436
|
+
/**
|
|
1437
|
+
* Event emitted when requesting data for a new page.
|
|
1438
|
+
*
|
|
1439
|
+
* Fired in pagination and infinity scroll modes when the user navigates
|
|
1440
|
+
* to a different page or scrolls near the end of current data.
|
|
1441
|
+
*
|
|
1442
|
+
* @example
|
|
1443
|
+
* ```typescript
|
|
1444
|
+
* onPageChange(event: GridPageChangeEvent) {
|
|
1445
|
+
* this.loadData(event.page, event.rows);
|
|
1446
|
+
* }
|
|
1447
|
+
* ```
|
|
1448
|
+
*/
|
|
660
1449
|
pageChange = output();
|
|
661
|
-
/**
|
|
1450
|
+
/**
|
|
1451
|
+
* Event emitted when sort order changes.
|
|
1452
|
+
*
|
|
1453
|
+
* Fired when a user clicks on a sortable column header.
|
|
1454
|
+
* Contains the sort key and direction (asc/desc).
|
|
1455
|
+
*
|
|
1456
|
+
* @example
|
|
1457
|
+
* ```typescript
|
|
1458
|
+
* onSortChange(event: GridSortEvent<User>) {
|
|
1459
|
+
* this.data = sortBy(this.data, event.key, event.order);
|
|
1460
|
+
* }
|
|
1461
|
+
* ```
|
|
1462
|
+
*/
|
|
662
1463
|
sortChange = output();
|
|
663
|
-
/**
|
|
1464
|
+
/**
|
|
1465
|
+
* Event emitted when selected rows change.
|
|
1466
|
+
*
|
|
1467
|
+
* Fired when a user selects or deselects rows.
|
|
1468
|
+
* Contains an array of currently selected row keys.
|
|
1469
|
+
*/
|
|
664
1470
|
selectChange = output();
|
|
665
|
-
/**
|
|
1471
|
+
/**
|
|
1472
|
+
* Event emitted when a row is clicked.
|
|
1473
|
+
*
|
|
1474
|
+
* Contains the clicked row data and its index.
|
|
1475
|
+
*/
|
|
666
1476
|
rowClick = output();
|
|
667
|
-
/**
|
|
1477
|
+
/**
|
|
1478
|
+
* Event emitted when a cell is clicked.
|
|
1479
|
+
*
|
|
1480
|
+
* Contains the clicked row, column configuration, and row index.
|
|
1481
|
+
*/
|
|
668
1482
|
cellClick = output();
|
|
669
1483
|
vm = inject((DataGridVm));
|
|
670
1484
|
selector = new Selector();
|
|
671
1485
|
rootEl = viewChild('root', ...(ngDevMode ? [{ debugName: "rootEl" }] : []));
|
|
672
1486
|
scrollEl = viewChild('scroll', ...(ngDevMode ? [{ debugName: "scrollEl" }] : []));
|
|
673
1487
|
headerEl = viewChild('header', ...(ngDevMode ? [{ debugName: "headerEl" }] : []));
|
|
674
|
-
|
|
1488
|
+
cellTypedSlotRefs = contentChildren(DataGridTypeCellTemplateDirective, ...(ngDevMode ? [{ debugName: "cellTypedSlotRefs" }] : []));
|
|
1489
|
+
cellDataSlotRefs = contentChildren(DataGridCellTemplateDirective, ...(ngDevMode ? [{ debugName: "cellDataSlotRefs" }] : []));
|
|
675
1490
|
headerSlotRefs = contentChildren(DataGridHeaderTemplateDirective, ...(ngDevMode ? [{ debugName: "headerSlotRefs" }] : []));
|
|
676
1491
|
emptySlotRefs = contentChildren(DataGridCellEmptyDirective, ...(ngDevMode ? [{ debugName: "emptySlotRefs" }] : []));
|
|
677
1492
|
loadingSlotRefs = contentChildren(DataGridCellLoadingDirective, ...(ngDevMode ? [{ debugName: "loadingSlotRefs" }] : []));
|
|
1493
|
+
sortIcSlotRefs = contentChildren(DataGridSortIconDirective, ...(ngDevMode ? [{ debugName: "sortIcSlotRefs" }] : []));
|
|
1494
|
+
expanderIcSlotRefs = contentChildren(DataGridExpanderIconDirective, ...(ngDevMode ? [{ debugName: "expanderIcSlotRefs" }] : []));
|
|
678
1495
|
emptyTpl = computed(() => this.emptySlotRefs()?.[0], ...(ngDevMode ? [{ debugName: "emptyTpl" }] : []));
|
|
679
1496
|
loadingTpl = computed(() => this.loadingSlotRefs()?.[0], ...(ngDevMode ? [{ debugName: "loadingTpl" }] : []));
|
|
1497
|
+
sortTpl = computed(() => this.sortIcSlotRefs()?.at(0), ...(ngDevMode ? [{ debugName: "sortTpl" }] : []));
|
|
1498
|
+
expanderTpl = computed(() => this.expanderIcSlotRefs()?.at(0), ...(ngDevMode ? [{ debugName: "expanderTpl" }] : []));
|
|
680
1499
|
visibleRows = signal([], ...(ngDevMode ? [{ debugName: "visibleRows" }] : []));
|
|
681
1500
|
startIndex = 0;
|
|
682
1501
|
headerHeight = signal(40, ...(ngDevMode ? [{ debugName: "headerHeight" }] : []));
|
|
1502
|
+
// todo someday
|
|
1503
|
+
// protected fillHeight = signal<number | null>(null);
|
|
683
1504
|
expanderMap = signal(new Map(), ...(ngDevMode ? [{ debugName: "expanderMap" }] : []));
|
|
684
1505
|
extendedColumns = computed(() => {
|
|
685
1506
|
const hasSelection = this.selection().mode !== 'none';
|
|
@@ -689,6 +1510,24 @@ class DataGrid {
|
|
|
689
1510
|
hasIndex && newColumns.push(GRID_INDEX_COLUMN);
|
|
690
1511
|
return [...newColumns, ...this.columns()];
|
|
691
1512
|
}, ...(ngDevMode ? [{ debugName: "extendedColumns" }] : []));
|
|
1513
|
+
/**
|
|
1514
|
+
* Computed CSS height value based on height setting.
|
|
1515
|
+
*/
|
|
1516
|
+
styleHeight = computed(() => {
|
|
1517
|
+
const height = this.height();
|
|
1518
|
+
if (typeof height === 'number') {
|
|
1519
|
+
return `${height}px`;
|
|
1520
|
+
// todo someday
|
|
1521
|
+
// } else if (height === 'fill') {
|
|
1522
|
+
// const h = this.fillHeight();
|
|
1523
|
+
//
|
|
1524
|
+
// return h === null ? null : `${h}px`;
|
|
1525
|
+
}
|
|
1526
|
+
else if (height === 'full') {
|
|
1527
|
+
return '100%';
|
|
1528
|
+
}
|
|
1529
|
+
return 'var(--re-data-grid-height)';
|
|
1530
|
+
}, ...(ngDevMode ? [{ debugName: "styleHeight" }] : []));
|
|
692
1531
|
hideSbTimeout;
|
|
693
1532
|
currentSortField;
|
|
694
1533
|
currentSortOrder = 'asc';
|
|
@@ -726,12 +1565,18 @@ class DataGrid {
|
|
|
726
1565
|
clearTimeout(this.hideSbTimeout);
|
|
727
1566
|
});
|
|
728
1567
|
}
|
|
729
|
-
get styleHeight() {
|
|
730
|
-
return this.fillHeight() ? null : 'auto';
|
|
731
|
-
}
|
|
732
1568
|
resolvePinnedData(pr) {
|
|
733
1569
|
return typeof pr.data === 'function' ? pr.data() : pr.data;
|
|
734
1570
|
}
|
|
1571
|
+
/**
|
|
1572
|
+
* Handles column header click for sorting.
|
|
1573
|
+
*
|
|
1574
|
+
* Toggles sort order between ascending and descending for the clicked column.
|
|
1575
|
+
* If a different column is clicked, it resets to ascending order.
|
|
1576
|
+
* Emits sortChange event with the current sort state.
|
|
1577
|
+
*
|
|
1578
|
+
* @param col - Column configuration that was clicked
|
|
1579
|
+
*/
|
|
735
1580
|
onSort(col) {
|
|
736
1581
|
if (!col.sortKey) {
|
|
737
1582
|
return;
|
|
@@ -745,6 +1590,16 @@ class DataGrid {
|
|
|
745
1590
|
}
|
|
746
1591
|
this.sortChange.emit({ key: this.currentSortField, order: this.currentSortOrder });
|
|
747
1592
|
}
|
|
1593
|
+
/**
|
|
1594
|
+
* Handles cell click events.
|
|
1595
|
+
*
|
|
1596
|
+
* Emits cellClick event and handles row selection if selection mode is enabled.
|
|
1597
|
+
* Updates the selection state and emits selectChange event accordingly.
|
|
1598
|
+
*
|
|
1599
|
+
* @param row - Data row that was clicked
|
|
1600
|
+
* @param col - Column configuration of the clicked cell
|
|
1601
|
+
* @param index - Row index in the dataset
|
|
1602
|
+
*/
|
|
748
1603
|
onCellClick(row, col, index) {
|
|
749
1604
|
this.cellClick.emit({ row, col, index });
|
|
750
1605
|
if (this.selection().mode !== 'none') {
|
|
@@ -756,6 +1611,14 @@ class DataGrid {
|
|
|
756
1611
|
const keys = this.expanderMap();
|
|
757
1612
|
return keys.has(column.key);
|
|
758
1613
|
}
|
|
1614
|
+
/**
|
|
1615
|
+
* Handles column expand/collapse toggle.
|
|
1616
|
+
*
|
|
1617
|
+
* Toggles the expanded state of a column and updates the visibility
|
|
1618
|
+
* of all dependent columns that reference this column via expandBy property.
|
|
1619
|
+
*
|
|
1620
|
+
* @param column - Column configuration to expand or collapse
|
|
1621
|
+
*/
|
|
759
1622
|
onExpand(column) {
|
|
760
1623
|
const expanded = !this.expanderMap().get(column.key);
|
|
761
1624
|
this.expanderMap.update((prev) => {
|
|
@@ -766,6 +1629,15 @@ class DataGrid {
|
|
|
766
1629
|
const columns = this.extendedColumns().map((col) => col.expandBy === column.key ? { ...col, visible: expanded } : col);
|
|
767
1630
|
this.vm.columns.set(columns);
|
|
768
1631
|
}
|
|
1632
|
+
/**
|
|
1633
|
+
* Handles vertical scroll events and updates visible rows.
|
|
1634
|
+
*
|
|
1635
|
+
* Implements virtual scrolling by calculating which rows should be rendered
|
|
1636
|
+
* based on the current scroll position. Also handles infinite scroll data loading
|
|
1637
|
+
* when the user scrolls near the end of available data.
|
|
1638
|
+
*
|
|
1639
|
+
* @param initial - Whether this is the initial scroll calculation
|
|
1640
|
+
*/
|
|
769
1641
|
onVerticalScroll(initial = false) {
|
|
770
1642
|
const el = this.scrollEl()?.nativeElement;
|
|
771
1643
|
if (!el)
|
|
@@ -805,6 +1677,15 @@ class DataGrid {
|
|
|
805
1677
|
this.hideScrollbarSoon();
|
|
806
1678
|
}
|
|
807
1679
|
onHorizontalScroll() { }
|
|
1680
|
+
/**
|
|
1681
|
+
* Handles mouse down event on scrollbar thumb for drag scrolling.
|
|
1682
|
+
*
|
|
1683
|
+
* Initiates dragging mode and sets up mouse move/up event listeners
|
|
1684
|
+
* to track thumb position and update scroll position accordingly.
|
|
1685
|
+
* Automatically cleans up listeners when dragging ends.
|
|
1686
|
+
*
|
|
1687
|
+
* @param e - Mouse down event from a scrollbar thumb element
|
|
1688
|
+
*/
|
|
808
1689
|
onThumbDown(e) {
|
|
809
1690
|
e.preventDefault();
|
|
810
1691
|
e.stopPropagation();
|
|
@@ -877,9 +1758,12 @@ class DataGrid {
|
|
|
877
1758
|
this.selector.selection.set(this.selection());
|
|
878
1759
|
}
|
|
879
1760
|
initRefs() {
|
|
880
|
-
this.
|
|
1761
|
+
this.cellTypedSlotRefs()?.forEach((dir) => this.vm.globalTypeCellTpls.set(dir.type(), dir.tpl));
|
|
1762
|
+
this.cellDataSlotRefs()?.forEach((dir) => this.vm.globalDataCellTpls.set(dir.key(), dir.tpl));
|
|
881
1763
|
const headerMap = new Map();
|
|
1764
|
+
const cellsMap = new Map();
|
|
882
1765
|
this.headerSlotRefs()?.forEach((h) => headerMap.set(h.key(), h.tpl));
|
|
1766
|
+
this.cellDataSlotRefs()?.forEach((h) => cellsMap.set(h.key(), h.tpl));
|
|
883
1767
|
this.extendedColumns()?.forEach((c) => {
|
|
884
1768
|
if (!c.headerTemplate && headerMap.has(c.key)) {
|
|
885
1769
|
c.headerTemplate = headerMap.get(c.key);
|
|
@@ -934,17 +1818,17 @@ class DataGrid {
|
|
|
934
1818
|
this.vm.columns.set(columns);
|
|
935
1819
|
this.expanderMap.set(map);
|
|
936
1820
|
}
|
|
937
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.
|
|
938
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.3", type: DataGrid, isStandalone: true, selector: "re-data-grid", inputs: { data: { classPropertyName: "data", publicName: "data", isSignal: true, isRequired: false, transformFunction: null }, columns: { classPropertyName: "columns", publicName: "columns", isSignal: true, isRequired: false, transformFunction: null }, mode: { classPropertyName: "mode", publicName: "mode", isSignal: true, isRequired: false, transformFunction: null }, pinnedRows: { classPropertyName: "pinnedRows", publicName: "pinnedRows", isSignal: true, isRequired: false, transformFunction: null }, hasIndexColumn: { classPropertyName: "hasIndexColumn", publicName: "hasIndexColumn", isSignal: true, isRequired: false, transformFunction: null }, selection: { classPropertyName: "selection", publicName: "selection", isSignal: true, isRequired: false, transformFunction: null }, pageSize: { classPropertyName: "pageSize", publicName: "pageSize", isSignal: true, isRequired: false, transformFunction: null }, rowHeight: { classPropertyName: "rowHeight", publicName: "rowHeight", isSignal: true, isRequired: false, transformFunction: null }, fillHeight: { classPropertyName: "fillHeight", publicName: "fillHeight", isSignal: true, isRequired: false, transformFunction: null }, virtualBuffer: { classPropertyName: "virtualBuffer", publicName: "virtualBuffer", isSignal: true, isRequired: false, transformFunction: null }, loading: { classPropertyName: "loading", publicName: "loading", isSignal: true, isRequired: false, transformFunction: null }, rowKey: { classPropertyName: "rowKey", publicName: "rowKey", isSignal: true, isRequired: false, transformFunction: null }, pageStartFromZero: { classPropertyName: "pageStartFromZero", publicName: "pageStartFromZero", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { pageChange: "pageChange", sortChange: "sortChange", selectChange: "selectChange", rowClick: "rowClick", cellClick: "cellClick" }, providers: [DataGridVm], queries: [{ propertyName: "cellSlotRefs", predicate: DataGridTypeCellTemplateDirective, isSignal: true }, { propertyName: "headerSlotRefs", predicate: DataGridHeaderTemplateDirective, isSignal: true }, { propertyName: "emptySlotRefs", predicate: DataGridCellEmptyDirective, isSignal: true }, { propertyName: "loadingSlotRefs", predicate: DataGridCellLoadingDirective, isSignal: true }], viewQueries: [{ propertyName: "rootEl", first: true, predicate: ["root"], descendants: true, isSignal: true }, { propertyName: "scrollEl", first: true, predicate: ["scroll"], descendants: true, isSignal: true }, { propertyName: "headerEl", first: true, predicate: ["header"], descendants: true, isSignal: true }], ngImport: i0, template: "@let items = data();\n@let empty = !loading() && !items?.length;\n@let notEmpty = !!items?.length;\n\n@let pinnedTopH = vm.pinnedTop().length * rowHeight();\n@let pinnedBottomH = vm.pinnedBottom().length * rowHeight();\n\n<div\n #root\n class=\"re-dg-root\"\n [class.fill]=\"fillHeight()\"\n [class.loading]=\"loading()\"\n [style.height]=\"styleHeight\"\n role=\"table\"\n>\n @if (loading()) {\n <div class=\"re-dg-loader\">\n @let loadingTemplate = loadingTpl();\n\n @if (loadingTemplate?.tpl) {\n <ng-container [ngTemplateOutlet]=\"loadingTemplate!.tpl\" />\n } @else {\n <span class=\"re-dg-loader-text\">\u0417\u0430\u0433\u0440\u0443\u0437\u043A\u0430\u2026</span>\n }\n </div>\n }\n\n <div\n #scroll\n class=\"re-dg-body\"\n role=\"rowgroup\"\n (mouseenter)=\"showScrollbar()\"\n (mouseleave)=\"hideScrollbarSoon()\"\n >\n <div class=\"re-dg-header\" role=\"rowgroup\">\n <div #header class=\"re-dg-row re-dg-header-row\" role=\"row\">\n @for (col of vm.columnsToShow(); track col.key) {\n <div\n class=\"re-dg-header-cell\"\n role=\"columnheader\"\n [class.sortable]=\"!!col.sortKey\"\n [class.active-sort]=\"currentSortField && (currentSortField === col.sortKey)\"\n [class.sticky-left]=\"vm.isStickyLeft(col.key)\"\n [class.sticky-right]=\"vm.isStickyRight(col.key)\"\n [style.left.px]=\"vm.stickyOffset(col.key, 'left')\"\n [style.right.px]=\"vm.stickyOffset(col.key, 'right')\"\n [style.width.px]=\"vm.widthByKey(col.key)\"\n [style.min-width.px]=\"col.minWidth || null\"\n [style.max-width.px]=\"col.maxWidth || null\"\n [style.justify-content]=\"col.align || 'left'\"\n [attr.aria-sort]=\"ariaSort(col)\"\n [attr.tabindex]=\"col.sortKey ? 0 : -1\"\n [title]=\"col.header\"\n (click)=\"col.sortKey && onSort(col)\"\n (keydown.enter)=\"col.sortKey && onSort(col)\"\n >\n {{currentSortField }} {{ col.sortKey}}\n @if (col.headerTemplate) {\n <ng-container\n [ngTemplateOutlet]=\"col.headerTemplate\"\n [ngTemplateOutletContext]=\"{ $implicit: col.header }\"\n />\n } @else {\n @let isCheckbox = 'type' in col && col.type === 'checkbox';\n @let isMultiSelect = selection().mode === 'multi';\n\n @if (isCheckbox && isMultiSelect) {\n <re-checkbox-ic mixable [state]=\"selector.isAllSelected()\" (click)=\"selector.selectAll()\" />\n } @else {\n <span class=\"re-dg-header-text\">{{ col.header }}</span>\n }\n\n @if (col.sortKey) {\n <span class=\"re-dg-sort-ind\">\n <re-sort-ic [direction]=\"currentSortField === col.sortKey ? currentSortOrder : 'desc'\" />\n </span>\n }\n\n @if (isExpandable(col)) {\n <button (click)=\"$event.stopPropagation(); onExpand(col)\">\n <re-expand-ic [expanded]=\"expanderMap().get(col.key)\" />\n </button>\n }\n }\n </div>\n }\n </div>\n\n <!-- PINNED TOP ROWS -->\n @if (notEmpty) {\n @for (pr of vm.pinnedTop(); track trackPinnedRow(pr)) {\n <div class=\"re-dg-row re-dg-pinned re-dg-top\" role=\"row\">\n @if (pr.rowTemplate) {\n <ng-container\n [ngTemplateOutlet]=\"pr.rowTemplate!\"\n [ngTemplateOutletContext]=\"{ $implicit: resolvePinnedData(pr) }\"\n />\n } @else {\n @for (col of vm.columnsToShow(); track col.key) {\n <div\n class=\"re-dg-cell\"\n role=\"cell\"\n [class.sticky-left]=\"vm.isStickyLeft(col.key)\"\n [class.sticky-right]=\"vm.isStickyRight(col.key)\"\n [style.left.px]=\"vm.stickyOffset(col.key, 'left')\"\n [style.right.px]=\"vm.stickyOffset(col.key, 'right')\"\n [style.justify-items]=\"col.align || 'left'\"\n [style.height.px]=\"rowHeight()\"\n [style.width.px]=\"vm.widthByKey(col.key)\"\n >\n {{ resolvePinnedData(pr)[col.key] ?? '' }}\n </div>\n }\n }\n </div>\n }\n }\n </div>\n\n @if (empty) {\n @let emptyTemplate = emptyTpl()?.tpl;\n\n <div class=\"re-dg-empty\">\n @if (emptyTemplate) {\n <ng-container [ngTemplateOutlet]=\"emptyTemplate\" />\n } @else {\n <span class=\"re-dg-empty-text\">\u041D\u0435\u0442 \u0434\u0430\u043D\u043D\u044B\u0445</span>\n }\n </div>\n }\n\n <!-- Content -->\n @if (notEmpty) {\n <div class=\"re-dg-spacer\" [style.height.px]=\"items.length * rowHeight() - pinnedBottomH\"></div>\n\n @for (row of visibleRows(); track trackByRow(row); let vi = $index) {\n <div\n class=\"re-dg-row re-dg-data-row\"\n role=\"row\"\n [style.height.px]=\"rowHeight()\"\n [style.top.px]=\"(startIndex + vi) * rowHeight() + pinnedTopH + headerHeight()\"\n [attr.tabindex]=\"0\"\n (click)=\"$event.stopPropagation(); rowClick.emit({ row, index: startIndex + vi })\"\n (keydown.enter)=\"$event.stopPropagation(); rowClick.emit({ row, index: startIndex + vi })\"\n >\n @for (col of vm.columnsToShow(); track col.key) {\n <div\n class=\"re-dg-cell\"\n role=\"cell\"\n [class]=\"cellClass(col, row)\"\n [class.sticky-left]=\"vm.isStickyLeft(col.key)\"\n [class.sticky-right]=\"vm.isStickyRight(col.key)\"\n [style.left.px]=\"vm.stickyOffset(col.key, 'left')\"\n [style.right.px]=\"vm.stickyOffset(col.key, 'right')\"\n [style.justify-items]=\"col.align || 'left'\"\n [style.text-align]=\"col.align || 'left'\"\n [style.width.px]=\"vm.widthByKey(col.key)\"\n [attr.tabindex]=\"0\"\n (click)=\"onCellClick(row, col, startIndex + vi);\"\n (keydown.enter)=\"onCellClick(row, col, startIndex + vi)\"\n >\n @let isCheckbox = 'type' in col && col.type === 'checkbox';\n\n @if (isCheckbox) {\n <re-checkbox-ic mixable=\"false\" [state]=\"selector.isSelected(row)\" />\n } @else {\n <re-data-grid-cell [index]=\"startIndex + vi\" [item]=\"row\" [column]=\"col\" />\n }\n </div>\n }\n </div>\n }\n }\n\n <!-- PINNED BOTTOM ROWS -->\n @if (notEmpty) {\n <div class=\"re-dg-footer\" role=\"rowgroup\">\n @for (pr of vm.pinnedBottom(); track trackPinnedRow(pr)) {\n <div class=\"re-dg-row re-dg-pinned re-dg-bottom\" role=\"row\">\n @if (pr.rowTemplate) {\n <ng-container\n [ngTemplateOutlet]=\"pr.rowTemplate!\"\n [ngTemplateOutletContext]=\"{ $implicit: resolvePinnedData(pr) }\"\n />\n } @else {\n @for (col of vm.columnsToShow(); track col.key) {\n <div\n class=\"re-dg-cell\"\n role=\"cell\"\n [class.sticky-left]=\"vm.isStickyLeft(col.key)\"\n [class.sticky-right]=\"vm.isStickyRight(col.key)\"\n [style.left.px]=\"vm.stickyOffset(col.key, 'left')\"\n [style.right.px]=\"vm.stickyOffset(col.key, 'right')\"\n [style.justify-items]=\"col.align || 'left'\"\n [style.height.px]=\"rowHeight()\"\n [style.width.px]=\"vm.widthByKey(col.key)\"\n >\n {{ resolvePinnedData(pr)[col.key] ?? '' }}\n </div>\n }\n }\n </div>\n }\n </div>\n }\n </div>\n\n <!-- Overlay scrollbar -->\n <div class=\"re-dg-scrollbar\" [class.visible]=\"vm.scrollbarVisible()\">\n <div\n class=\"re-dg-scrollbar-thumb\"\n role=\"scrollbar\"\n aria-orientation=\"vertical\"\n aria-hidden=\"false\"\n [style.height.px]=\"vm.thumbHeightPx()\"\n [style.transform]=\"'translateY(' + vm.thumbTopPx() + 'px)'\"\n (mousedown)=\"onThumbDown($event)\"\n ></div>\n </div>\n</div>\n", styles: [":host{--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: #fff;--re-data-grid-active: #2a90f4;--re-data-grid-empty-color: #777;--re-data-grid-loading-color: #444;--re-data-grid-loading-surface: rgba(255, 255, 255, .5);--re-data-grid-scrollbar-size: 10px;--re-data-grid-scrollbar-offset: 2px;--re-data-grid-scrollbar-thumb-size: 8px;--re-data-grid-scrollbar-thumb-color: rgba(0, 0, 0, .35);--re-data-grid-scrollbar-thumb-rounded: 4px;--re-data-grid-header-height: 40px;--re-data-grid-header-separator-color: #ccc;--re-data-grid-header-separator: 1px solid var(--re-data-grid-header-separator-color);--re-data-grid-header-surface: #fff;--re-data-grid-header-cell-font-weight: 600;--re-data-grid-header-cell-font-size: .8rem;--re-data-grid-header-cell-color: #000;--re-data-grid-header-cell-surface: #fafafa;--re-data-grid-footer-separator-color: #ccc;--re-data-grid-footer-separator: 1px solid var(--re-data-grid-footer-separator-color);--re-data-grid-footer-surface: #fff;--re-data-grid-row-separator-color: #bbb;--re-data-grid-row-separator: 1px solid var(--re-data-grid-row-separator-color);--re-data-grid-row-odd-surface: var(--re-data-grid-cell-surface);--re-data-grid-column-separator-color: transparent;--re-data-grid-column-separator: 1px solid var(--re-data-grid-column-separator-color);--re-data-grid-column-odd-surface: var(--re-data-grid-cell-surface);--re-data-grid-cell-paddings: .4rem .625rem;--re-data-grid-cell-font-weight: 400;--re-data-grid-cell-font-size: .75rem;--re-data-grid-cell-color: #000;--re-data-grid-cell-surface: #fff;--re-data-grid-sticky-header-cell-surface: #fff;--re-data-grid-sticky-cell-surface: #fdfdfd;--re-data-grid-sticky-cell-left-shadow: 2px 0 2px rgba(0, 0, 0, .03);--re-data-grid-sticky-cell-right-shadow: -2px 0 2px rgba(0, 0, 0, .03);--re-data-grid-pinned-surface: #fcfcfc;--re-data-grid-pinned-separator-color: #eee;--re-data-grid-pinned-separator: 1px solid var(--re-data-grid-pinned-separator-color);--re-data-grid-expander-color: var(--primary-color, currentColor)}:host :host,:host *,:host *:before,:host *:after{box-sizing:border-box;outline:none}:host button{outline:none}.re-dg-root{position:relative;display:block;width:100%;height:var(--re-data-grid-height);max-height:var(--re-data-grid-height);border-radius:var(--re-data-grid-rounded);border:var(--re-data-grid-separator)}.re-dg-root.fill{display:flex;flex-direction:column;height:100%}.re-dg-root.loading{pointer-events:none;-webkit-user-select:none;user-select:none;cursor:wait}.re-dg-root.loading .re-dg-body{overflow:hidden}.re-dg-root.loading .re-dg-scrollbar{display:none!important}.re-dg-root.loading .re-dg-loader{pointer-events:all}.re-dg-body{position:relative;flex:1 1 auto;min-width:0;height:var(--re-data-grid-height);border:var(--re-data-grid-separator);border-radius:var(--re-data-grid-rounded);background-color:var(--re-data-grid-surface);overflow:auto;scrollbar-width:none;-ms-overflow-style:none}.re-dg-body::-webkit-scrollbar{width:0;height:0}.re-dg-header,.re-dg-footer{position:sticky;z-index:3}.re-dg-header{top:0;background-color:var(--re-data-grid-header-surface)}.re-dg-header-row{min-height:var(--re-data-grid-header-height)}.re-dg-footer{bottom:0;border-radius:0 0 var(--re-data-grid-rounded) var(--re-data-grid-rounded);background-color:var(--re-data-grid-footer-surface)}.re-dg-row{position:relative;display:flex}.re-dg-data-row{position:absolute;left:0;right:0;cursor:default}.re-dg-cell,.re-dg-header-cell{display:flex;flex:0 0 auto;align-items:center;padding:var(--re-data-grid-cell-paddings);border-right:var(--re-data-grid-column-separator);text-overflow:ellipsis;white-space:nowrap;overflow:hidden}.re-dg-cell{width:100%;border-bottom:var(--re-data-grid-row-separator);font-weight:var(--re-data-grid-cell-font-weight);font-size:var(--re-data-grid-cell-font-size);color:var(--re-data-grid-cell-color);background-color:var(--re-data-grid-cell-surface)}.re-dg-row:nth-child(odd) .re-dg-cell{background-color:var(--re-data-grid-row-odd-surface)}.re-dg-cell:nth-child(odd){background-color:var(--re-data-grid-column-odd-surface)}.re-dg-bottom>.re-dg-cell{border-top:var(--re-data-grid-footer-separator)}.re-dg-header-cell{align-items:center;gap:.75rem;border-bottom:var(--re-data-grid-header-separator);font-weight:var(--re-data-grid-header-cell-font-weight);font-size:var(--re-data-grid-header-cell-font-size);color:var(--re-data-grid-header-cell-color);background:var(--re-data-grid-header-cell-surface);-webkit-user-select:none;user-select:none;transition:color .3s ease-in-out}.re-dg-header-cell:first-child{border-radius:var(--re-data-grid-rounded) 0 0 0}.re-dg-header-cell:last-child{border-radius:0 var(--re-data-grid-rounded) 0 0}.re-dg-data-row:last-child .re-dg-cell:first-child{border-radius:0 0 0 var(--re-data-grid-rounded)}.re-dg-data-row:last-child .re-dg-cell:last-child{border-radius:0 0 var(--re-data-grid-rounded) 0}.re-dg-header-cell .re-dg-header-text{display:-webkit-box;max-height:2.4em;line-height:1.2;white-space:normal;-webkit-box-orient:vertical;-webkit-line-clamp:2;overflow:hidden}.re-dg-row.re-dg-pinned>.re-dg-cell{border-bottom:var(--re-data-grid-pinned-separator);background-color:var(--re-data-grid-pinned-surface)}.re-dg-row .re-dg-header-cell.sticky-left{box-shadow:var(--re-data-grid-sticky-cell-left-shadow);background-color:var(--re-data-grid-sticky-header-cell-surface)}.re-dg-row .re-dg-cell.sticky-left{box-shadow:var(--re-data-grid-sticky-cell-left-shadow);background-color:var(--re-data-grid-sticky-cell-surface)}.re-dg-row .re-dg-header-cell.sticky-right{box-shadow:var(--re-data-grid-sticky-cell-right-shadow);background-color:var(--re-data-grid-sticky-header-cell-surface)}.re-dg-row .re-dg-cell.sticky-right{box-shadow:var(--re-data-grid-sticky-cell-right-shadow);background-color:var(--re-data-grid-sticky-cell-surface)}.sticky-left,.sticky-right{position:sticky;z-index:2}.sortable{cursor:pointer}.active-sort{color:var(--re-data-grid-active)}.re-dg-sort-ind{margin-left:6px}.re-dg-empty{position:absolute;inset:0;display:flex;place-items:center;color:var(--re-data-grid-empty-color)}.re-dg-empty-text{width:100%;text-align:center}.re-dg-loader{position:absolute;inset:0;top:var(--re-data-grid-header-height);display:grid;place-items:center;background-color:var(--re-data-grid-loading-surface);color:var(--re-data-grid-loading-color);z-index:5}.re-dg-scrollbar{position:absolute;right:0;top:0;bottom:0;opacity:0;transition:opacity .15s ease-in-out;pointer-events:none;z-index:4}.re-dg-scrollbar.visible{opacity:1}.re-dg-scrollbar-thumb{position:absolute;right:var(--re-data-grid-scrollbar-offset);width:var(--re-data-grid-scrollbar-thumb-size);border-radius:var(--re-data-grid-scrollbar-thumb-rounded);background:var(--re-data-grid-scrollbar-thumb-color);pointer-events:auto;-webkit-user-select:none;user-select:none}.re-dg-spacer{width:1px}.re-dg-top{top:0}.re-dg-bottom{bottom:0}\n"], dependencies: [{ kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "component", type: DataGridCellComponent, selector: "re-data-grid-cell", inputs: ["index", "item", "column"] }, { kind: "component", type: SortIcon, selector: "re-sort-ic", inputs: ["direction"] }, { kind: "component", type: ExpandIcon, selector: "re-expand-ic", inputs: ["expanded"] }, { kind: "component", type: CheckboxIcon, selector: "re-checkbox-ic", inputs: ["state", "disabled"] }] });
|
|
1821
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.1", ngImport: i0, type: DataGrid, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
1822
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.1.1", type: DataGrid, isStandalone: true, selector: "re-data-grid", inputs: { data: { classPropertyName: "data", publicName: "data", isSignal: true, isRequired: false, transformFunction: null }, columns: { classPropertyName: "columns", publicName: "columns", isSignal: true, isRequired: false, transformFunction: null }, mode: { classPropertyName: "mode", publicName: "mode", isSignal: true, isRequired: false, transformFunction: null }, pinnedRows: { classPropertyName: "pinnedRows", publicName: "pinnedRows", isSignal: true, isRequired: false, transformFunction: null }, hasIndexColumn: { classPropertyName: "hasIndexColumn", publicName: "hasIndexColumn", isSignal: true, isRequired: false, transformFunction: null }, selection: { classPropertyName: "selection", publicName: "selection", isSignal: true, isRequired: false, transformFunction: null }, pageSize: { classPropertyName: "pageSize", publicName: "pageSize", isSignal: true, isRequired: false, transformFunction: null }, rowHeight: { classPropertyName: "rowHeight", publicName: "rowHeight", isSignal: true, isRequired: false, transformFunction: null }, height: { classPropertyName: "height", publicName: "height", isSignal: true, isRequired: false, transformFunction: null }, virtualBuffer: { classPropertyName: "virtualBuffer", publicName: "virtualBuffer", isSignal: true, isRequired: false, transformFunction: null }, loading: { classPropertyName: "loading", publicName: "loading", isSignal: true, isRequired: false, transformFunction: null }, rowKey: { classPropertyName: "rowKey", publicName: "rowKey", isSignal: true, isRequired: false, transformFunction: null }, pageStartFromZero: { classPropertyName: "pageStartFromZero", publicName: "pageStartFromZero", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { pageChange: "pageChange", sortChange: "sortChange", selectChange: "selectChange", rowClick: "rowClick", cellClick: "cellClick" }, providers: [DataGridVm], queries: [{ propertyName: "cellTypedSlotRefs", predicate: DataGridTypeCellTemplateDirective, isSignal: true }, { propertyName: "cellDataSlotRefs", predicate: DataGridCellTemplateDirective, isSignal: true }, { propertyName: "headerSlotRefs", predicate: DataGridHeaderTemplateDirective, isSignal: true }, { propertyName: "emptySlotRefs", predicate: DataGridCellEmptyDirective, isSignal: true }, { propertyName: "loadingSlotRefs", predicate: DataGridCellLoadingDirective, isSignal: true }, { propertyName: "sortIcSlotRefs", predicate: DataGridSortIconDirective, isSignal: true }, { propertyName: "expanderIcSlotRefs", predicate: DataGridExpanderIconDirective, isSignal: true }], viewQueries: [{ propertyName: "rootEl", first: true, predicate: ["root"], descendants: true, isSignal: true }, { propertyName: "scrollEl", first: true, predicate: ["scroll"], descendants: true, isSignal: true }, { propertyName: "headerEl", first: true, predicate: ["header"], descendants: true, isSignal: true }], ngImport: i0, template: "@let items = data();\n@let empty = !loading() && !items?.length;\n@let notEmpty = !!items?.length;\n\n@let pinnedTopH = vm.pinnedTop().length * rowHeight();\n@let pinnedBottomH = vm.pinnedBottom().length * rowHeight();\n\n<div\n #root\n class=\"re-dg-root\"\n [class.loading]=\"loading()\"\n [style.height]=\"styleHeight()\"\n role=\"table\"\n>\n @if (loading()) {\n <div class=\"re-dg-loader\">\n @let loadingTemplate = loadingTpl();\n\n @if (loadingTemplate?.tpl) {\n <ng-container [ngTemplateOutlet]=\"loadingTemplate!.tpl\" />\n } @else {\n <span class=\"re-dg-loader-text\">\u0417\u0430\u0433\u0440\u0443\u0437\u043A\u0430\u2026</span>\n }\n </div>\n }\n\n <div\n #scroll\n class=\"re-dg-body\"\n role=\"rowgroup\"\n (mouseenter)=\"showScrollbar()\"\n (mouseleave)=\"hideScrollbarSoon()\"\n >\n <div class=\"re-dg-header\" role=\"rowgroup\">\n <div #header class=\"re-dg-row re-dg-header-row\" role=\"row\">\n @for (col of vm.columnsToShow(); track col.key) {\n <div\n class=\"re-dg-header-cell\"\n role=\"columnheader\"\n [class.sortable]=\"!!col.sortKey\"\n [class.active-sort]=\"currentSortField && (currentSortField === col.sortKey)\"\n [class.sticky-left]=\"vm.isStickyLeft(col.key)\"\n [class.sticky-right]=\"vm.isStickyRight(col.key)\"\n [style.left.px]=\"vm.stickyOffset(col.key, 'left')\"\n [style.right.px]=\"vm.stickyOffset(col.key, 'right')\"\n [style.width.px]=\"vm.widthByKey(col.key)\"\n [style.min-width.px]=\"col.minWidth || null\"\n [style.max-width.px]=\"col.maxWidth || null\"\n [style.justify-content]=\"col.align || 'left'\"\n [attr.aria-sort]=\"ariaSort(col)\"\n [attr.tabindex]=\"col.sortKey ? 0 : -1\"\n [title]=\"col.header\"\n (click)=\"col.sortKey && onSort(col)\"\n (keydown.enter)=\"col.sortKey && onSort(col)\"\n >\n @if (col.headerTemplate) {\n <ng-container\n [ngTemplateOutlet]=\"col.headerTemplate\"\n [ngTemplateOutletContext]=\"{ $implicit: col.header }\"\n />\n } @else {\n @let isCheckbox = 'type' in col && col.type === 'checkbox';\n @let isMultiSelect = selection().mode === 'multi';\n\n @if (isCheckbox && isMultiSelect) {\n <re-checkbox-ic [state]=\"selector.isAllSelected()\" (click)=\"selector.selectAll()\" />\n } @else {\n <span class=\"re-dg-header-text\">{{ col.header }}</span>\n }\n\n @if (col.sortKey) {\n <span class=\"re-dg-sort-ind\">\n @let direction = currentSortField === col.sortKey ? currentSortOrder : undefined;\n\n @if (sortTpl()) {\n <ng-container\n [ngTemplateOutlet]=\"sortTpl()!.tpl\"\n [ngTemplateOutletContext]=\"{ $implicit: direction }\"\n />\n } @else {\n <re-sort-ic [direction]=\"direction\" />\n }\n </span>\n }\n\n @if (isExpandable(col)) {\n <button (click)=\"$event.stopPropagation(); onExpand(col)\">\n @let expanded = expanderMap().get(col.key);\n\n @if (expanderTpl()) {\n <ng-container\n [ngTemplateOutlet]=\"expanderTpl()!.tpl\"\n [ngTemplateOutletContext]=\"{ $implicit: expanded }\" />\n } @else {\n <re-expand-ic [expanded]=\"expanded\" />\n }\n </button>\n }\n }\n </div>\n }\n </div>\n\n <!-- PINNED TOP ROWS -->\n @if (notEmpty) {\n @for (pr of vm.pinnedTop(); track trackPinnedRow(pr)) {\n <div class=\"re-dg-row re-dg-pinned re-dg-top\" role=\"row\">\n @if (pr.rowTemplate) {\n <ng-container\n [ngTemplateOutlet]=\"pr.rowTemplate!\"\n [ngTemplateOutletContext]=\"{ $implicit: resolvePinnedData(pr) }\"\n />\n } @else {\n @for (col of vm.columnsToShow(); track col.key) {\n <div\n class=\"re-dg-cell\"\n role=\"cell\"\n [class.sticky-left]=\"vm.isStickyLeft(col.key)\"\n [class.sticky-right]=\"vm.isStickyRight(col.key)\"\n [style.left.px]=\"vm.stickyOffset(col.key, 'left')\"\n [style.right.px]=\"vm.stickyOffset(col.key, 'right')\"\n [style.justify-items]=\"col.align || 'left'\"\n [style.height.px]=\"rowHeight()\"\n [style.width.px]=\"vm.widthByKey(col.key)\"\n >\n {{ resolvePinnedData(pr)[col.key] ?? '' }}\n </div>\n }\n }\n </div>\n }\n }\n </div>\n\n @if (empty) {\n @let emptyTemplate = emptyTpl()?.tpl;\n\n <div class=\"re-dg-empty\">\n @if (emptyTemplate) {\n <ng-container [ngTemplateOutlet]=\"emptyTemplate\" />\n } @else {\n <span class=\"re-dg-empty-text\">\u041D\u0435\u0442 \u0434\u0430\u043D\u043D\u044B\u0445</span>\n }\n </div>\n }\n\n <!-- Content -->\n @if (notEmpty) {\n <div class=\"re-dg-spacer\" [style.height.px]=\"items.length * rowHeight() - pinnedBottomH\"></div>\n\n @for (row of visibleRows(); track trackByRow(row); let vi = $index) {\n <div\n class=\"re-dg-row re-dg-data-row\"\n role=\"row\"\n [style.height.px]=\"rowHeight()\"\n [style.top.px]=\"(startIndex + vi) * rowHeight() + pinnedTopH + headerHeight()\"\n [attr.tabindex]=\"0\"\n (click)=\"$event.stopPropagation(); rowClick.emit({ row, index: startIndex + vi })\"\n (keydown.enter)=\"$event.stopPropagation(); rowClick.emit({ row, index: startIndex + vi })\"\n >\n @for (col of vm.columnsToShow(); track col.key) {\n <div\n class=\"re-dg-cell\"\n role=\"cell\"\n [class.expanded]=\"!!col.expandBy\"\n [class]=\"cellClass(col, row)\"\n [class.sticky-left]=\"vm.isStickyLeft(col.key)\"\n [class.sticky-right]=\"vm.isStickyRight(col.key)\"\n [style.left.px]=\"vm.stickyOffset(col.key, 'left')\"\n [style.right.px]=\"vm.stickyOffset(col.key, 'right')\"\n [style.justify-items]=\"col.align || 'left'\"\n [style.text-align]=\"col.align || 'left'\"\n [style.width.px]=\"vm.widthByKey(col.key)\"\n [attr.tabindex]=\"0\"\n (click)=\"onCellClick(row, col, startIndex + vi);\"\n (keydown.enter)=\"onCellClick(row, col, startIndex + vi)\"\n >\n @let isCheckbox = 'type' in col && col.type === 'checkbox';\n\n @if (isCheckbox) {\n <re-checkbox-ic [state]=\"selector.isSelected(row)\" />\n } @else {\n <re-data-grid-cell [index]=\"startIndex + vi\" [item]=\"row\" [column]=\"col\" />\n }\n </div>\n }\n </div>\n }\n }\n\n <!-- PINNED BOTTOM ROWS -->\n @if (notEmpty) {\n <div class=\"re-dg-footer\" role=\"rowgroup\">\n @for (pr of vm.pinnedBottom(); track trackPinnedRow(pr)) {\n <div class=\"re-dg-row re-dg-pinned re-dg-bottom\" role=\"row\">\n @if (pr.rowTemplate) {\n <ng-container\n [ngTemplateOutlet]=\"pr.rowTemplate!\"\n [ngTemplateOutletContext]=\"{ $implicit: resolvePinnedData(pr) }\"\n />\n } @else {\n @for (col of vm.columnsToShow(); track col.key) {\n <div\n class=\"re-dg-cell\"\n role=\"cell\"\n [class.sticky-left]=\"vm.isStickyLeft(col.key)\"\n [class.sticky-right]=\"vm.isStickyRight(col.key)\"\n [style.left.px]=\"vm.stickyOffset(col.key, 'left')\"\n [style.right.px]=\"vm.stickyOffset(col.key, 'right')\"\n [style.justify-items]=\"col.align || 'left'\"\n [style.height.px]=\"rowHeight()\"\n [style.width.px]=\"vm.widthByKey(col.key)\"\n >\n {{ resolvePinnedData(pr)[col.key] ?? '' }}\n </div>\n }\n }\n </div>\n }\n </div>\n }\n </div>\n\n <!-- Overlay scrollbar -->\n <div class=\"re-dg-scrollbar\" [class.visible]=\"vm.scrollbarVisible()\">\n <div\n class=\"re-dg-scrollbar-thumb\"\n role=\"scrollbar\"\n aria-orientation=\"vertical\"\n aria-hidden=\"false\"\n [style.height.px]=\"vm.thumbHeightPx()\"\n [style.transform]=\"'translateY(' + vm.thumbTopPx() + 'px)'\"\n (mousedown)=\"onThumbDown($event)\"\n ></div>\n </div>\n</div>\n\n", styles: [":host{--re-data-grid-min-height: 200px;--re-data-grid-height: 400px;--re-data-grid-rounded: var(--radius-md, 6px);--re-data-grid-separator-color: var(--border-color);--re-data-grid-separator: 1px solid var(--re-data-grid-separator-color);--re-data-grid-surface: var(--surface-primary, #fff);--re-data-grid-active: var(--primary-color, #2a90f4);--re-data-grid-empty-color: #777;--re-data-grid-empty-surface: transparent;--re-data-grid-loading-color: #444;--re-data-grid-loading-surface: rgba(255, 255, 255, .5);--re-data-grid-scrollbar-size: 10px;--re-data-grid-scrollbar-offset: 2px;--re-data-grid-scrollbar-thumb-size: 8px;--re-data-grid-scrollbar-thumb-color: rgba(0, 0, 0, .35);--re-data-grid-scrollbar-thumb-rounded: 4px;--re-data-grid-header-height: 40px;--re-data-grid-header-separator-color: #ccc;--re-data-grid-header-separator: 1px solid var(--re-data-grid-header-separator-color);--re-data-grid-header-surface: #fff;--re-data-grid-header-cell-font-weight: 600;--re-data-grid-header-cell-font-size: .8rem;--re-data-grid-header-cell-color: #000;--re-data-grid-header-cell-surface: #fafafa;--re-data-grid-footer-separator-color: #ccc;--re-data-grid-footer-separator: 1px solid var(--re-data-grid-footer-separator-color);--re-data-grid-footer-surface: #fff;--re-data-grid-row-separator-color: #bbb;--re-data-grid-row-separator: 1px solid var(--re-data-grid-row-separator-color);--re-data-grid-row-odd-surface: var(--re-data-grid-cell-surface);--re-data-grid-column-separator-color: transparent;--re-data-grid-column-separator: 1px solid var(--re-data-grid-column-separator-color);--re-data-grid-column-odd-surface: var(--re-data-grid-cell-surface);--re-data-grid-cell-paddings: .4rem .625rem;--re-data-grid-cell-font-weight: 400;--re-data-grid-cell-font-size: .75rem;--re-data-grid-cell-color: #000;--re-data-grid-cell-surface: #fff;--re-data-grid-sticky-header-cell-surface: #fff;--re-data-grid-sticky-cell-surface: #fdfdfd;--re-data-grid-sticky-cell-left-shadow: 2px 0 2px rgba(0, 0, 0, .03);--re-data-grid-sticky-cell-right-shadow: -2px 0 2px rgba(0, 0, 0, .03);--re-data-grid-pinned-surface: #fcfcfc;--re-data-grid-pinned-separator-color: #eee;--re-data-grid-pinned-separator: 1px solid var(--re-data-grid-pinned-separator-color);--re-data-grid-expander-color: var(--primary-color, currentColor);--re-data-grid-expanded-color: var(--re-data-grid-cell-color, #000);--re-data-grid-expanded-surface: var(--re-data-grid-cell-surface, #fff);display:block;min-height:0}:host,:host *,:host *:before,:host *:after{box-sizing:border-box;outline:none}:host button{outline:none}.re-dg-root{position:relative;display:flex;flex-direction:column;width:100%;min-height:var(--re-data-grid-min-height);border-radius:var(--re-data-grid-rounded);border:var(--re-data-grid-separator)}.re-dg-root.fill{display:block}.re-dg-root.loading{pointer-events:none;-webkit-user-select:none;user-select:none;cursor:wait}.re-dg-root.loading .re-dg-body{overflow:hidden}.re-dg-root.loading .re-dg-scrollbar{display:none!important}.re-dg-root.loading .re-dg-loader{pointer-events:all}.re-dg-body{position:relative;flex:1 1 auto;min-height:0;min-width:0;height:inherit;border:var(--re-data-grid-separator);border-radius:var(--re-data-grid-rounded);background-color:var(--re-data-grid-surface);overflow:auto;scrollbar-width:none;-ms-overflow-style:none}.re-dg-body::-webkit-scrollbar{width:0;height:0}.re-dg-header,.re-dg-footer{position:sticky;z-index:3}.re-dg-header{top:0;background-color:var(--re-data-grid-header-surface)}.re-dg-header-row{min-height:var(--re-data-grid-header-height)}.re-dg-footer{bottom:0;border-radius:0 0 var(--re-data-grid-rounded) var(--re-data-grid-rounded);background-color:var(--re-data-grid-footer-surface)}.re-dg-row{position:relative;display:flex}.re-dg-data-row{position:absolute;left:0;right:0;cursor:default}.re-dg-cell,.re-dg-header-cell{display:flex;flex:0 0 auto;align-items:center;padding:var(--re-data-grid-cell-paddings);border-right:var(--re-data-grid-column-separator);text-overflow:ellipsis;white-space:nowrap;overflow:hidden}.re-dg-cell{width:100%;border-bottom:var(--re-data-grid-row-separator);font-weight:var(--re-data-grid-cell-font-weight);font-size:var(--re-data-grid-cell-font-size);color:var(--re-data-grid-cell-color);background-color:var(--re-data-grid-cell-surface)}.re-dg-row:nth-child(odd) .re-dg-cell{background-color:var(--re-data-grid-row-odd-surface)}.re-dg-cell:nth-child(odd){background-color:var(--re-data-grid-column-odd-surface)}.re-dg-bottom>.re-dg-cell{border-top:var(--re-data-grid-footer-separator)}.re-dg-header-cell{align-items:center;gap:.75rem;border-bottom:var(--re-data-grid-header-separator);font-weight:var(--re-data-grid-header-cell-font-weight);font-size:var(--re-data-grid-header-cell-font-size);color:var(--re-data-grid-header-cell-color);background:var(--re-data-grid-header-cell-surface);-webkit-user-select:none;user-select:none;transition:color .3s ease-in-out}.re-dg-header-cell:first-child{border-radius:var(--re-data-grid-rounded) 0 0 0}.re-dg-header-cell:last-child{border-radius:0 var(--re-data-grid-rounded) 0 0}.re-dg-data-row:last-child .re-dg-cell:first-child{border-radius:0 0 0 var(--re-data-grid-rounded)}.re-dg-data-row:last-child .re-dg-cell:last-child{border-radius:0 0 var(--re-data-grid-rounded) 0}.re-dg-header-cell .re-dg-header-text{display:-webkit-box;max-height:2.4em;line-height:1.2;white-space:normal;-webkit-box-orient:vertical;-webkit-line-clamp:2;overflow:hidden}.re-dg-row.re-dg-pinned>.re-dg-cell{border-bottom:var(--re-data-grid-pinned-separator);background-color:var(--re-data-grid-pinned-surface)}.re-dg-row .re-dg-header-cell.sticky-left{box-shadow:var(--re-data-grid-sticky-cell-left-shadow);background-color:var(--re-data-grid-sticky-header-cell-surface)}.re-dg-row .re-dg-cell.sticky-left{box-shadow:var(--re-data-grid-sticky-cell-left-shadow);background-color:var(--re-data-grid-sticky-cell-surface)}.re-dg-row .re-dg-header-cell.sticky-right{box-shadow:var(--re-data-grid-sticky-cell-right-shadow);background-color:var(--re-data-grid-sticky-header-cell-surface)}.re-dg-row .re-dg-cell.sticky-right{box-shadow:var(--re-data-grid-sticky-cell-right-shadow);background-color:var(--re-data-grid-sticky-cell-surface)}.sticky-left,.sticky-right{position:sticky;z-index:2}.sortable{cursor:pointer}.active-sort{color:var(--re-data-grid-active)}.re-dg-sort-ind{margin-left:6px}.re-dg-data-row .re-dg-cell.expanded{color:var(--re-data-grid-expanded-color);background:var(--re-data-grid-expanded-surface)}.re-dg-empty{position:absolute;inset:0;display:grid;place-items:center;height:inherit;width:100%;border-radius:var(--re-data-grid-rounded);color:var(--re-data-grid-empty-color);background:var(--re-data-grid-empty-surface)}.re-dg-empty-text{width:100%;text-align:center}.re-dg-loader{position:absolute;inset:0;display:grid;place-items:center;height:inherit;border-radius:var(--re-data-grid-rounded);background-color:var(--re-data-grid-loading-surface);color:var(--re-data-grid-loading-color);z-index:5}.re-dg-scrollbar{position:absolute;right:0;top:0;bottom:0;opacity:0;transition:opacity .15s ease-in-out;pointer-events:none;z-index:4}.re-dg-scrollbar.visible{opacity:1}.re-dg-scrollbar-thumb{position:absolute;right:var(--re-data-grid-scrollbar-offset);width:var(--re-data-grid-scrollbar-thumb-size);border-radius:var(--re-data-grid-scrollbar-thumb-rounded);background:var(--re-data-grid-scrollbar-thumb-color);pointer-events:auto;-webkit-user-select:none;user-select:none}.re-dg-spacer{width:1px}.re-dg-top{top:0}.re-dg-bottom{bottom:0}\n"], dependencies: [{ kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "component", type: DataGridCellComponent, selector: "re-data-grid-cell", inputs: ["index", "item", "column"] }, { kind: "component", type: SortIcon, selector: "re-sort-ic", inputs: ["direction"] }, { kind: "component", type: ExpandIcon, selector: "re-expand-ic", inputs: ["expanded"] }, { kind: "component", type: CheckboxIcon, selector: "re-checkbox-ic", inputs: ["state", "disabled"] }] });
|
|
939
1823
|
}
|
|
940
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.
|
|
1824
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.1", ngImport: i0, type: DataGrid, decorators: [{
|
|
941
1825
|
type: Component,
|
|
942
|
-
args: [{ selector: 're-data-grid', imports: [NgTemplateOutlet, DataGridCellComponent, SortIcon, ExpandIcon, CheckboxIcon], providers: [DataGridVm], template: "@let items = data();\n@let empty = !loading() && !items?.length;\n@let notEmpty = !!items?.length;\n\n@let pinnedTopH = vm.pinnedTop().length * rowHeight();\n@let pinnedBottomH = vm.pinnedBottom().length * rowHeight();\n\n<div\n #root\n class=\"re-dg-root\"\n [class.fill]=\"fillHeight()\"\n [class.loading]=\"loading()\"\n [style.height]=\"styleHeight\"\n role=\"table\"\n>\n @if (loading()) {\n <div class=\"re-dg-loader\">\n @let loadingTemplate = loadingTpl();\n\n @if (loadingTemplate?.tpl) {\n <ng-container [ngTemplateOutlet]=\"loadingTemplate!.tpl\" />\n } @else {\n <span class=\"re-dg-loader-text\">\u0417\u0430\u0433\u0440\u0443\u0437\u043A\u0430\u2026</span>\n }\n </div>\n }\n\n <div\n #scroll\n class=\"re-dg-body\"\n role=\"rowgroup\"\n (mouseenter)=\"showScrollbar()\"\n (mouseleave)=\"hideScrollbarSoon()\"\n >\n <div class=\"re-dg-header\" role=\"rowgroup\">\n <div #header class=\"re-dg-row re-dg-header-row\" role=\"row\">\n @for (col of vm.columnsToShow(); track col.key) {\n <div\n class=\"re-dg-header-cell\"\n role=\"columnheader\"\n [class.sortable]=\"!!col.sortKey\"\n [class.active-sort]=\"currentSortField && (currentSortField === col.sortKey)\"\n [class.sticky-left]=\"vm.isStickyLeft(col.key)\"\n [class.sticky-right]=\"vm.isStickyRight(col.key)\"\n [style.left.px]=\"vm.stickyOffset(col.key, 'left')\"\n [style.right.px]=\"vm.stickyOffset(col.key, 'right')\"\n [style.width.px]=\"vm.widthByKey(col.key)\"\n [style.min-width.px]=\"col.minWidth || null\"\n [style.max-width.px]=\"col.maxWidth || null\"\n [style.justify-content]=\"col.align || 'left'\"\n [attr.aria-sort]=\"ariaSort(col)\"\n [attr.tabindex]=\"col.sortKey ? 0 : -1\"\n [title]=\"col.header\"\n (click)=\"col.sortKey && onSort(col)\"\n (keydown.enter)=\"col.sortKey && onSort(col)\"\n >\n {{currentSortField }} {{ col.sortKey}}\n @if (col.headerTemplate) {\n <ng-container\n [ngTemplateOutlet]=\"col.headerTemplate\"\n [ngTemplateOutletContext]=\"{ $implicit: col.header }\"\n />\n } @else {\n @let isCheckbox = 'type' in col && col.type === 'checkbox';\n @let isMultiSelect = selection().mode === 'multi';\n\n @if (isCheckbox && isMultiSelect) {\n <re-checkbox-ic mixable [state]=\"selector.isAllSelected()\" (click)=\"selector.selectAll()\" />\n } @else {\n <span class=\"re-dg-header-text\">{{ col.header }}</span>\n }\n\n @if (col.sortKey) {\n <span class=\"re-dg-sort-ind\">\n <re-sort-ic [direction]=\"currentSortField === col.sortKey ? currentSortOrder : 'desc'\" />\n </span>\n }\n\n @if (isExpandable(col)) {\n <button (click)=\"$event.stopPropagation(); onExpand(col)\">\n <re-expand-ic [expanded]=\"expanderMap().get(col.key)\" />\n </button>\n }\n }\n </div>\n }\n </div>\n\n <!-- PINNED TOP ROWS -->\n @if (notEmpty) {\n @for (pr of vm.pinnedTop(); track trackPinnedRow(pr)) {\n <div class=\"re-dg-row re-dg-pinned re-dg-top\" role=\"row\">\n @if (pr.rowTemplate) {\n <ng-container\n [ngTemplateOutlet]=\"pr.rowTemplate!\"\n [ngTemplateOutletContext]=\"{ $implicit: resolvePinnedData(pr) }\"\n />\n } @else {\n @for (col of vm.columnsToShow(); track col.key) {\n <div\n class=\"re-dg-cell\"\n role=\"cell\"\n [class.sticky-left]=\"vm.isStickyLeft(col.key)\"\n [class.sticky-right]=\"vm.isStickyRight(col.key)\"\n [style.left.px]=\"vm.stickyOffset(col.key, 'left')\"\n [style.right.px]=\"vm.stickyOffset(col.key, 'right')\"\n [style.justify-items]=\"col.align || 'left'\"\n [style.height.px]=\"rowHeight()\"\n [style.width.px]=\"vm.widthByKey(col.key)\"\n >\n {{ resolvePinnedData(pr)[col.key] ?? '' }}\n </div>\n }\n }\n </div>\n }\n }\n </div>\n\n @if (empty) {\n @let emptyTemplate = emptyTpl()?.tpl;\n\n <div class=\"re-dg-empty\">\n @if (emptyTemplate) {\n <ng-container [ngTemplateOutlet]=\"emptyTemplate\" />\n } @else {\n <span class=\"re-dg-empty-text\">\u041D\u0435\u0442 \u0434\u0430\u043D\u043D\u044B\u0445</span>\n }\n </div>\n }\n\n <!-- Content -->\n @if (notEmpty) {\n <div class=\"re-dg-spacer\" [style.height.px]=\"items.length * rowHeight() - pinnedBottomH\"></div>\n\n @for (row of visibleRows(); track trackByRow(row); let vi = $index) {\n <div\n class=\"re-dg-row re-dg-data-row\"\n role=\"row\"\n [style.height.px]=\"rowHeight()\"\n [style.top.px]=\"(startIndex + vi) * rowHeight() + pinnedTopH + headerHeight()\"\n [attr.tabindex]=\"0\"\n (click)=\"$event.stopPropagation(); rowClick.emit({ row, index: startIndex + vi })\"\n (keydown.enter)=\"$event.stopPropagation(); rowClick.emit({ row, index: startIndex + vi })\"\n >\n @for (col of vm.columnsToShow(); track col.key) {\n <div\n class=\"re-dg-cell\"\n role=\"cell\"\n [class]=\"cellClass(col, row)\"\n [class.sticky-left]=\"vm.isStickyLeft(col.key)\"\n [class.sticky-right]=\"vm.isStickyRight(col.key)\"\n [style.left.px]=\"vm.stickyOffset(col.key, 'left')\"\n [style.right.px]=\"vm.stickyOffset(col.key, 'right')\"\n [style.justify-items]=\"col.align || 'left'\"\n [style.text-align]=\"col.align || 'left'\"\n [style.width.px]=\"vm.widthByKey(col.key)\"\n [attr.tabindex]=\"0\"\n (click)=\"onCellClick(row, col, startIndex + vi);\"\n (keydown.enter)=\"onCellClick(row, col, startIndex + vi)\"\n >\n @let isCheckbox = 'type' in col && col.type === 'checkbox';\n\n @if (isCheckbox) {\n <re-checkbox-ic mixable=\"false\" [state]=\"selector.isSelected(row)\" />\n } @else {\n <re-data-grid-cell [index]=\"startIndex + vi\" [item]=\"row\" [column]=\"col\" />\n }\n </div>\n }\n </div>\n }\n }\n\n <!-- PINNED BOTTOM ROWS -->\n @if (notEmpty) {\n <div class=\"re-dg-footer\" role=\"rowgroup\">\n @for (pr of vm.pinnedBottom(); track trackPinnedRow(pr)) {\n <div class=\"re-dg-row re-dg-pinned re-dg-bottom\" role=\"row\">\n @if (pr.rowTemplate) {\n <ng-container\n [ngTemplateOutlet]=\"pr.rowTemplate!\"\n [ngTemplateOutletContext]=\"{ $implicit: resolvePinnedData(pr) }\"\n />\n } @else {\n @for (col of vm.columnsToShow(); track col.key) {\n <div\n class=\"re-dg-cell\"\n role=\"cell\"\n [class.sticky-left]=\"vm.isStickyLeft(col.key)\"\n [class.sticky-right]=\"vm.isStickyRight(col.key)\"\n [style.left.px]=\"vm.stickyOffset(col.key, 'left')\"\n [style.right.px]=\"vm.stickyOffset(col.key, 'right')\"\n [style.justify-items]=\"col.align || 'left'\"\n [style.height.px]=\"rowHeight()\"\n [style.width.px]=\"vm.widthByKey(col.key)\"\n >\n {{ resolvePinnedData(pr)[col.key] ?? '' }}\n </div>\n }\n }\n </div>\n }\n </div>\n }\n </div>\n\n <!-- Overlay scrollbar -->\n <div class=\"re-dg-scrollbar\" [class.visible]=\"vm.scrollbarVisible()\">\n <div\n class=\"re-dg-scrollbar-thumb\"\n role=\"scrollbar\"\n aria-orientation=\"vertical\"\n aria-hidden=\"false\"\n [style.height.px]=\"vm.thumbHeightPx()\"\n [style.transform]=\"'translateY(' + vm.thumbTopPx() + 'px)'\"\n (mousedown)=\"onThumbDown($event)\"\n ></div>\n </div>\n</div>\n", styles: [":host{--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: #fff;--re-data-grid-active: #2a90f4;--re-data-grid-empty-color: #777;--re-data-grid-loading-color: #444;--re-data-grid-loading-surface: rgba(255, 255, 255, .5);--re-data-grid-scrollbar-size: 10px;--re-data-grid-scrollbar-offset: 2px;--re-data-grid-scrollbar-thumb-size: 8px;--re-data-grid-scrollbar-thumb-color: rgba(0, 0, 0, .35);--re-data-grid-scrollbar-thumb-rounded: 4px;--re-data-grid-header-height: 40px;--re-data-grid-header-separator-color: #ccc;--re-data-grid-header-separator: 1px solid var(--re-data-grid-header-separator-color);--re-data-grid-header-surface: #fff;--re-data-grid-header-cell-font-weight: 600;--re-data-grid-header-cell-font-size: .8rem;--re-data-grid-header-cell-color: #000;--re-data-grid-header-cell-surface: #fafafa;--re-data-grid-footer-separator-color: #ccc;--re-data-grid-footer-separator: 1px solid var(--re-data-grid-footer-separator-color);--re-data-grid-footer-surface: #fff;--re-data-grid-row-separator-color: #bbb;--re-data-grid-row-separator: 1px solid var(--re-data-grid-row-separator-color);--re-data-grid-row-odd-surface: var(--re-data-grid-cell-surface);--re-data-grid-column-separator-color: transparent;--re-data-grid-column-separator: 1px solid var(--re-data-grid-column-separator-color);--re-data-grid-column-odd-surface: var(--re-data-grid-cell-surface);--re-data-grid-cell-paddings: .4rem .625rem;--re-data-grid-cell-font-weight: 400;--re-data-grid-cell-font-size: .75rem;--re-data-grid-cell-color: #000;--re-data-grid-cell-surface: #fff;--re-data-grid-sticky-header-cell-surface: #fff;--re-data-grid-sticky-cell-surface: #fdfdfd;--re-data-grid-sticky-cell-left-shadow: 2px 0 2px rgba(0, 0, 0, .03);--re-data-grid-sticky-cell-right-shadow: -2px 0 2px rgba(0, 0, 0, .03);--re-data-grid-pinned-surface: #fcfcfc;--re-data-grid-pinned-separator-color: #eee;--re-data-grid-pinned-separator: 1px solid var(--re-data-grid-pinned-separator-color);--re-data-grid-expander-color: var(--primary-color, currentColor)}:host :host,:host *,:host *:before,:host *:after{box-sizing:border-box;outline:none}:host button{outline:none}.re-dg-root{position:relative;display:block;width:100%;height:var(--re-data-grid-height);max-height:var(--re-data-grid-height);border-radius:var(--re-data-grid-rounded);border:var(--re-data-grid-separator)}.re-dg-root.fill{display:flex;flex-direction:column;height:100%}.re-dg-root.loading{pointer-events:none;-webkit-user-select:none;user-select:none;cursor:wait}.re-dg-root.loading .re-dg-body{overflow:hidden}.re-dg-root.loading .re-dg-scrollbar{display:none!important}.re-dg-root.loading .re-dg-loader{pointer-events:all}.re-dg-body{position:relative;flex:1 1 auto;min-width:0;height:var(--re-data-grid-height);border:var(--re-data-grid-separator);border-radius:var(--re-data-grid-rounded);background-color:var(--re-data-grid-surface);overflow:auto;scrollbar-width:none;-ms-overflow-style:none}.re-dg-body::-webkit-scrollbar{width:0;height:0}.re-dg-header,.re-dg-footer{position:sticky;z-index:3}.re-dg-header{top:0;background-color:var(--re-data-grid-header-surface)}.re-dg-header-row{min-height:var(--re-data-grid-header-height)}.re-dg-footer{bottom:0;border-radius:0 0 var(--re-data-grid-rounded) var(--re-data-grid-rounded);background-color:var(--re-data-grid-footer-surface)}.re-dg-row{position:relative;display:flex}.re-dg-data-row{position:absolute;left:0;right:0;cursor:default}.re-dg-cell,.re-dg-header-cell{display:flex;flex:0 0 auto;align-items:center;padding:var(--re-data-grid-cell-paddings);border-right:var(--re-data-grid-column-separator);text-overflow:ellipsis;white-space:nowrap;overflow:hidden}.re-dg-cell{width:100%;border-bottom:var(--re-data-grid-row-separator);font-weight:var(--re-data-grid-cell-font-weight);font-size:var(--re-data-grid-cell-font-size);color:var(--re-data-grid-cell-color);background-color:var(--re-data-grid-cell-surface)}.re-dg-row:nth-child(odd) .re-dg-cell{background-color:var(--re-data-grid-row-odd-surface)}.re-dg-cell:nth-child(odd){background-color:var(--re-data-grid-column-odd-surface)}.re-dg-bottom>.re-dg-cell{border-top:var(--re-data-grid-footer-separator)}.re-dg-header-cell{align-items:center;gap:.75rem;border-bottom:var(--re-data-grid-header-separator);font-weight:var(--re-data-grid-header-cell-font-weight);font-size:var(--re-data-grid-header-cell-font-size);color:var(--re-data-grid-header-cell-color);background:var(--re-data-grid-header-cell-surface);-webkit-user-select:none;user-select:none;transition:color .3s ease-in-out}.re-dg-header-cell:first-child{border-radius:var(--re-data-grid-rounded) 0 0 0}.re-dg-header-cell:last-child{border-radius:0 var(--re-data-grid-rounded) 0 0}.re-dg-data-row:last-child .re-dg-cell:first-child{border-radius:0 0 0 var(--re-data-grid-rounded)}.re-dg-data-row:last-child .re-dg-cell:last-child{border-radius:0 0 var(--re-data-grid-rounded) 0}.re-dg-header-cell .re-dg-header-text{display:-webkit-box;max-height:2.4em;line-height:1.2;white-space:normal;-webkit-box-orient:vertical;-webkit-line-clamp:2;overflow:hidden}.re-dg-row.re-dg-pinned>.re-dg-cell{border-bottom:var(--re-data-grid-pinned-separator);background-color:var(--re-data-grid-pinned-surface)}.re-dg-row .re-dg-header-cell.sticky-left{box-shadow:var(--re-data-grid-sticky-cell-left-shadow);background-color:var(--re-data-grid-sticky-header-cell-surface)}.re-dg-row .re-dg-cell.sticky-left{box-shadow:var(--re-data-grid-sticky-cell-left-shadow);background-color:var(--re-data-grid-sticky-cell-surface)}.re-dg-row .re-dg-header-cell.sticky-right{box-shadow:var(--re-data-grid-sticky-cell-right-shadow);background-color:var(--re-data-grid-sticky-header-cell-surface)}.re-dg-row .re-dg-cell.sticky-right{box-shadow:var(--re-data-grid-sticky-cell-right-shadow);background-color:var(--re-data-grid-sticky-cell-surface)}.sticky-left,.sticky-right{position:sticky;z-index:2}.sortable{cursor:pointer}.active-sort{color:var(--re-data-grid-active)}.re-dg-sort-ind{margin-left:6px}.re-dg-empty{position:absolute;inset:0;display:flex;place-items:center;color:var(--re-data-grid-empty-color)}.re-dg-empty-text{width:100%;text-align:center}.re-dg-loader{position:absolute;inset:0;top:var(--re-data-grid-header-height);display:grid;place-items:center;background-color:var(--re-data-grid-loading-surface);color:var(--re-data-grid-loading-color);z-index:5}.re-dg-scrollbar{position:absolute;right:0;top:0;bottom:0;opacity:0;transition:opacity .15s ease-in-out;pointer-events:none;z-index:4}.re-dg-scrollbar.visible{opacity:1}.re-dg-scrollbar-thumb{position:absolute;right:var(--re-data-grid-scrollbar-offset);width:var(--re-data-grid-scrollbar-thumb-size);border-radius:var(--re-data-grid-scrollbar-thumb-rounded);background:var(--re-data-grid-scrollbar-thumb-color);pointer-events:auto;-webkit-user-select:none;user-select:none}.re-dg-spacer{width:1px}.re-dg-top{top:0}.re-dg-bottom{bottom:0}\n"] }]
|
|
943
|
-
}], ctorParameters: () => [], propDecorators: { data: [{ type: i0.Input, args: [{ isSignal: true, alias: "data", required: false }] }], columns: [{ type: i0.Input, args: [{ isSignal: true, alias: "columns", required: false }] }], mode: [{ type: i0.Input, args: [{ isSignal: true, alias: "mode", required: false }] }], pinnedRows: [{ type: i0.Input, args: [{ isSignal: true, alias: "pinnedRows", required: false }] }], hasIndexColumn: [{ type: i0.Input, args: [{ isSignal: true, alias: "hasIndexColumn", required: false }] }], selection: [{ type: i0.Input, args: [{ isSignal: true, alias: "selection", required: false }] }], pageSize: [{ type: i0.Input, args: [{ isSignal: true, alias: "pageSize", required: false }] }], rowHeight: [{ type: i0.Input, args: [{ isSignal: true, alias: "rowHeight", required: false }] }],
|
|
1826
|
+
args: [{ selector: 're-data-grid', imports: [NgTemplateOutlet, DataGridCellComponent, SortIcon, ExpandIcon, CheckboxIcon], providers: [DataGridVm], template: "@let items = data();\n@let empty = !loading() && !items?.length;\n@let notEmpty = !!items?.length;\n\n@let pinnedTopH = vm.pinnedTop().length * rowHeight();\n@let pinnedBottomH = vm.pinnedBottom().length * rowHeight();\n\n<div\n #root\n class=\"re-dg-root\"\n [class.loading]=\"loading()\"\n [style.height]=\"styleHeight()\"\n role=\"table\"\n>\n @if (loading()) {\n <div class=\"re-dg-loader\">\n @let loadingTemplate = loadingTpl();\n\n @if (loadingTemplate?.tpl) {\n <ng-container [ngTemplateOutlet]=\"loadingTemplate!.tpl\" />\n } @else {\n <span class=\"re-dg-loader-text\">\u0417\u0430\u0433\u0440\u0443\u0437\u043A\u0430\u2026</span>\n }\n </div>\n }\n\n <div\n #scroll\n class=\"re-dg-body\"\n role=\"rowgroup\"\n (mouseenter)=\"showScrollbar()\"\n (mouseleave)=\"hideScrollbarSoon()\"\n >\n <div class=\"re-dg-header\" role=\"rowgroup\">\n <div #header class=\"re-dg-row re-dg-header-row\" role=\"row\">\n @for (col of vm.columnsToShow(); track col.key) {\n <div\n class=\"re-dg-header-cell\"\n role=\"columnheader\"\n [class.sortable]=\"!!col.sortKey\"\n [class.active-sort]=\"currentSortField && (currentSortField === col.sortKey)\"\n [class.sticky-left]=\"vm.isStickyLeft(col.key)\"\n [class.sticky-right]=\"vm.isStickyRight(col.key)\"\n [style.left.px]=\"vm.stickyOffset(col.key, 'left')\"\n [style.right.px]=\"vm.stickyOffset(col.key, 'right')\"\n [style.width.px]=\"vm.widthByKey(col.key)\"\n [style.min-width.px]=\"col.minWidth || null\"\n [style.max-width.px]=\"col.maxWidth || null\"\n [style.justify-content]=\"col.align || 'left'\"\n [attr.aria-sort]=\"ariaSort(col)\"\n [attr.tabindex]=\"col.sortKey ? 0 : -1\"\n [title]=\"col.header\"\n (click)=\"col.sortKey && onSort(col)\"\n (keydown.enter)=\"col.sortKey && onSort(col)\"\n >\n @if (col.headerTemplate) {\n <ng-container\n [ngTemplateOutlet]=\"col.headerTemplate\"\n [ngTemplateOutletContext]=\"{ $implicit: col.header }\"\n />\n } @else {\n @let isCheckbox = 'type' in col && col.type === 'checkbox';\n @let isMultiSelect = selection().mode === 'multi';\n\n @if (isCheckbox && isMultiSelect) {\n <re-checkbox-ic [state]=\"selector.isAllSelected()\" (click)=\"selector.selectAll()\" />\n } @else {\n <span class=\"re-dg-header-text\">{{ col.header }}</span>\n }\n\n @if (col.sortKey) {\n <span class=\"re-dg-sort-ind\">\n @let direction = currentSortField === col.sortKey ? currentSortOrder : undefined;\n\n @if (sortTpl()) {\n <ng-container\n [ngTemplateOutlet]=\"sortTpl()!.tpl\"\n [ngTemplateOutletContext]=\"{ $implicit: direction }\"\n />\n } @else {\n <re-sort-ic [direction]=\"direction\" />\n }\n </span>\n }\n\n @if (isExpandable(col)) {\n <button (click)=\"$event.stopPropagation(); onExpand(col)\">\n @let expanded = expanderMap().get(col.key);\n\n @if (expanderTpl()) {\n <ng-container\n [ngTemplateOutlet]=\"expanderTpl()!.tpl\"\n [ngTemplateOutletContext]=\"{ $implicit: expanded }\" />\n } @else {\n <re-expand-ic [expanded]=\"expanded\" />\n }\n </button>\n }\n }\n </div>\n }\n </div>\n\n <!-- PINNED TOP ROWS -->\n @if (notEmpty) {\n @for (pr of vm.pinnedTop(); track trackPinnedRow(pr)) {\n <div class=\"re-dg-row re-dg-pinned re-dg-top\" role=\"row\">\n @if (pr.rowTemplate) {\n <ng-container\n [ngTemplateOutlet]=\"pr.rowTemplate!\"\n [ngTemplateOutletContext]=\"{ $implicit: resolvePinnedData(pr) }\"\n />\n } @else {\n @for (col of vm.columnsToShow(); track col.key) {\n <div\n class=\"re-dg-cell\"\n role=\"cell\"\n [class.sticky-left]=\"vm.isStickyLeft(col.key)\"\n [class.sticky-right]=\"vm.isStickyRight(col.key)\"\n [style.left.px]=\"vm.stickyOffset(col.key, 'left')\"\n [style.right.px]=\"vm.stickyOffset(col.key, 'right')\"\n [style.justify-items]=\"col.align || 'left'\"\n [style.height.px]=\"rowHeight()\"\n [style.width.px]=\"vm.widthByKey(col.key)\"\n >\n {{ resolvePinnedData(pr)[col.key] ?? '' }}\n </div>\n }\n }\n </div>\n }\n }\n </div>\n\n @if (empty) {\n @let emptyTemplate = emptyTpl()?.tpl;\n\n <div class=\"re-dg-empty\">\n @if (emptyTemplate) {\n <ng-container [ngTemplateOutlet]=\"emptyTemplate\" />\n } @else {\n <span class=\"re-dg-empty-text\">\u041D\u0435\u0442 \u0434\u0430\u043D\u043D\u044B\u0445</span>\n }\n </div>\n }\n\n <!-- Content -->\n @if (notEmpty) {\n <div class=\"re-dg-spacer\" [style.height.px]=\"items.length * rowHeight() - pinnedBottomH\"></div>\n\n @for (row of visibleRows(); track trackByRow(row); let vi = $index) {\n <div\n class=\"re-dg-row re-dg-data-row\"\n role=\"row\"\n [style.height.px]=\"rowHeight()\"\n [style.top.px]=\"(startIndex + vi) * rowHeight() + pinnedTopH + headerHeight()\"\n [attr.tabindex]=\"0\"\n (click)=\"$event.stopPropagation(); rowClick.emit({ row, index: startIndex + vi })\"\n (keydown.enter)=\"$event.stopPropagation(); rowClick.emit({ row, index: startIndex + vi })\"\n >\n @for (col of vm.columnsToShow(); track col.key) {\n <div\n class=\"re-dg-cell\"\n role=\"cell\"\n [class.expanded]=\"!!col.expandBy\"\n [class]=\"cellClass(col, row)\"\n [class.sticky-left]=\"vm.isStickyLeft(col.key)\"\n [class.sticky-right]=\"vm.isStickyRight(col.key)\"\n [style.left.px]=\"vm.stickyOffset(col.key, 'left')\"\n [style.right.px]=\"vm.stickyOffset(col.key, 'right')\"\n [style.justify-items]=\"col.align || 'left'\"\n [style.text-align]=\"col.align || 'left'\"\n [style.width.px]=\"vm.widthByKey(col.key)\"\n [attr.tabindex]=\"0\"\n (click)=\"onCellClick(row, col, startIndex + vi);\"\n (keydown.enter)=\"onCellClick(row, col, startIndex + vi)\"\n >\n @let isCheckbox = 'type' in col && col.type === 'checkbox';\n\n @if (isCheckbox) {\n <re-checkbox-ic [state]=\"selector.isSelected(row)\" />\n } @else {\n <re-data-grid-cell [index]=\"startIndex + vi\" [item]=\"row\" [column]=\"col\" />\n }\n </div>\n }\n </div>\n }\n }\n\n <!-- PINNED BOTTOM ROWS -->\n @if (notEmpty) {\n <div class=\"re-dg-footer\" role=\"rowgroup\">\n @for (pr of vm.pinnedBottom(); track trackPinnedRow(pr)) {\n <div class=\"re-dg-row re-dg-pinned re-dg-bottom\" role=\"row\">\n @if (pr.rowTemplate) {\n <ng-container\n [ngTemplateOutlet]=\"pr.rowTemplate!\"\n [ngTemplateOutletContext]=\"{ $implicit: resolvePinnedData(pr) }\"\n />\n } @else {\n @for (col of vm.columnsToShow(); track col.key) {\n <div\n class=\"re-dg-cell\"\n role=\"cell\"\n [class.sticky-left]=\"vm.isStickyLeft(col.key)\"\n [class.sticky-right]=\"vm.isStickyRight(col.key)\"\n [style.left.px]=\"vm.stickyOffset(col.key, 'left')\"\n [style.right.px]=\"vm.stickyOffset(col.key, 'right')\"\n [style.justify-items]=\"col.align || 'left'\"\n [style.height.px]=\"rowHeight()\"\n [style.width.px]=\"vm.widthByKey(col.key)\"\n >\n {{ resolvePinnedData(pr)[col.key] ?? '' }}\n </div>\n }\n }\n </div>\n }\n </div>\n }\n </div>\n\n <!-- Overlay scrollbar -->\n <div class=\"re-dg-scrollbar\" [class.visible]=\"vm.scrollbarVisible()\">\n <div\n class=\"re-dg-scrollbar-thumb\"\n role=\"scrollbar\"\n aria-orientation=\"vertical\"\n aria-hidden=\"false\"\n [style.height.px]=\"vm.thumbHeightPx()\"\n [style.transform]=\"'translateY(' + vm.thumbTopPx() + 'px)'\"\n (mousedown)=\"onThumbDown($event)\"\n ></div>\n </div>\n</div>\n\n", styles: [":host{--re-data-grid-min-height: 200px;--re-data-grid-height: 400px;--re-data-grid-rounded: var(--radius-md, 6px);--re-data-grid-separator-color: var(--border-color);--re-data-grid-separator: 1px solid var(--re-data-grid-separator-color);--re-data-grid-surface: var(--surface-primary, #fff);--re-data-grid-active: var(--primary-color, #2a90f4);--re-data-grid-empty-color: #777;--re-data-grid-empty-surface: transparent;--re-data-grid-loading-color: #444;--re-data-grid-loading-surface: rgba(255, 255, 255, .5);--re-data-grid-scrollbar-size: 10px;--re-data-grid-scrollbar-offset: 2px;--re-data-grid-scrollbar-thumb-size: 8px;--re-data-grid-scrollbar-thumb-color: rgba(0, 0, 0, .35);--re-data-grid-scrollbar-thumb-rounded: 4px;--re-data-grid-header-height: 40px;--re-data-grid-header-separator-color: #ccc;--re-data-grid-header-separator: 1px solid var(--re-data-grid-header-separator-color);--re-data-grid-header-surface: #fff;--re-data-grid-header-cell-font-weight: 600;--re-data-grid-header-cell-font-size: .8rem;--re-data-grid-header-cell-color: #000;--re-data-grid-header-cell-surface: #fafafa;--re-data-grid-footer-separator-color: #ccc;--re-data-grid-footer-separator: 1px solid var(--re-data-grid-footer-separator-color);--re-data-grid-footer-surface: #fff;--re-data-grid-row-separator-color: #bbb;--re-data-grid-row-separator: 1px solid var(--re-data-grid-row-separator-color);--re-data-grid-row-odd-surface: var(--re-data-grid-cell-surface);--re-data-grid-column-separator-color: transparent;--re-data-grid-column-separator: 1px solid var(--re-data-grid-column-separator-color);--re-data-grid-column-odd-surface: var(--re-data-grid-cell-surface);--re-data-grid-cell-paddings: .4rem .625rem;--re-data-grid-cell-font-weight: 400;--re-data-grid-cell-font-size: .75rem;--re-data-grid-cell-color: #000;--re-data-grid-cell-surface: #fff;--re-data-grid-sticky-header-cell-surface: #fff;--re-data-grid-sticky-cell-surface: #fdfdfd;--re-data-grid-sticky-cell-left-shadow: 2px 0 2px rgba(0, 0, 0, .03);--re-data-grid-sticky-cell-right-shadow: -2px 0 2px rgba(0, 0, 0, .03);--re-data-grid-pinned-surface: #fcfcfc;--re-data-grid-pinned-separator-color: #eee;--re-data-grid-pinned-separator: 1px solid var(--re-data-grid-pinned-separator-color);--re-data-grid-expander-color: var(--primary-color, currentColor);--re-data-grid-expanded-color: var(--re-data-grid-cell-color, #000);--re-data-grid-expanded-surface: var(--re-data-grid-cell-surface, #fff);display:block;min-height:0}:host,:host *,:host *:before,:host *:after{box-sizing:border-box;outline:none}:host button{outline:none}.re-dg-root{position:relative;display:flex;flex-direction:column;width:100%;min-height:var(--re-data-grid-min-height);border-radius:var(--re-data-grid-rounded);border:var(--re-data-grid-separator)}.re-dg-root.fill{display:block}.re-dg-root.loading{pointer-events:none;-webkit-user-select:none;user-select:none;cursor:wait}.re-dg-root.loading .re-dg-body{overflow:hidden}.re-dg-root.loading .re-dg-scrollbar{display:none!important}.re-dg-root.loading .re-dg-loader{pointer-events:all}.re-dg-body{position:relative;flex:1 1 auto;min-height:0;min-width:0;height:inherit;border:var(--re-data-grid-separator);border-radius:var(--re-data-grid-rounded);background-color:var(--re-data-grid-surface);overflow:auto;scrollbar-width:none;-ms-overflow-style:none}.re-dg-body::-webkit-scrollbar{width:0;height:0}.re-dg-header,.re-dg-footer{position:sticky;z-index:3}.re-dg-header{top:0;background-color:var(--re-data-grid-header-surface)}.re-dg-header-row{min-height:var(--re-data-grid-header-height)}.re-dg-footer{bottom:0;border-radius:0 0 var(--re-data-grid-rounded) var(--re-data-grid-rounded);background-color:var(--re-data-grid-footer-surface)}.re-dg-row{position:relative;display:flex}.re-dg-data-row{position:absolute;left:0;right:0;cursor:default}.re-dg-cell,.re-dg-header-cell{display:flex;flex:0 0 auto;align-items:center;padding:var(--re-data-grid-cell-paddings);border-right:var(--re-data-grid-column-separator);text-overflow:ellipsis;white-space:nowrap;overflow:hidden}.re-dg-cell{width:100%;border-bottom:var(--re-data-grid-row-separator);font-weight:var(--re-data-grid-cell-font-weight);font-size:var(--re-data-grid-cell-font-size);color:var(--re-data-grid-cell-color);background-color:var(--re-data-grid-cell-surface)}.re-dg-row:nth-child(odd) .re-dg-cell{background-color:var(--re-data-grid-row-odd-surface)}.re-dg-cell:nth-child(odd){background-color:var(--re-data-grid-column-odd-surface)}.re-dg-bottom>.re-dg-cell{border-top:var(--re-data-grid-footer-separator)}.re-dg-header-cell{align-items:center;gap:.75rem;border-bottom:var(--re-data-grid-header-separator);font-weight:var(--re-data-grid-header-cell-font-weight);font-size:var(--re-data-grid-header-cell-font-size);color:var(--re-data-grid-header-cell-color);background:var(--re-data-grid-header-cell-surface);-webkit-user-select:none;user-select:none;transition:color .3s ease-in-out}.re-dg-header-cell:first-child{border-radius:var(--re-data-grid-rounded) 0 0 0}.re-dg-header-cell:last-child{border-radius:0 var(--re-data-grid-rounded) 0 0}.re-dg-data-row:last-child .re-dg-cell:first-child{border-radius:0 0 0 var(--re-data-grid-rounded)}.re-dg-data-row:last-child .re-dg-cell:last-child{border-radius:0 0 var(--re-data-grid-rounded) 0}.re-dg-header-cell .re-dg-header-text{display:-webkit-box;max-height:2.4em;line-height:1.2;white-space:normal;-webkit-box-orient:vertical;-webkit-line-clamp:2;overflow:hidden}.re-dg-row.re-dg-pinned>.re-dg-cell{border-bottom:var(--re-data-grid-pinned-separator);background-color:var(--re-data-grid-pinned-surface)}.re-dg-row .re-dg-header-cell.sticky-left{box-shadow:var(--re-data-grid-sticky-cell-left-shadow);background-color:var(--re-data-grid-sticky-header-cell-surface)}.re-dg-row .re-dg-cell.sticky-left{box-shadow:var(--re-data-grid-sticky-cell-left-shadow);background-color:var(--re-data-grid-sticky-cell-surface)}.re-dg-row .re-dg-header-cell.sticky-right{box-shadow:var(--re-data-grid-sticky-cell-right-shadow);background-color:var(--re-data-grid-sticky-header-cell-surface)}.re-dg-row .re-dg-cell.sticky-right{box-shadow:var(--re-data-grid-sticky-cell-right-shadow);background-color:var(--re-data-grid-sticky-cell-surface)}.sticky-left,.sticky-right{position:sticky;z-index:2}.sortable{cursor:pointer}.active-sort{color:var(--re-data-grid-active)}.re-dg-sort-ind{margin-left:6px}.re-dg-data-row .re-dg-cell.expanded{color:var(--re-data-grid-expanded-color);background:var(--re-data-grid-expanded-surface)}.re-dg-empty{position:absolute;inset:0;display:grid;place-items:center;height:inherit;width:100%;border-radius:var(--re-data-grid-rounded);color:var(--re-data-grid-empty-color);background:var(--re-data-grid-empty-surface)}.re-dg-empty-text{width:100%;text-align:center}.re-dg-loader{position:absolute;inset:0;display:grid;place-items:center;height:inherit;border-radius:var(--re-data-grid-rounded);background-color:var(--re-data-grid-loading-surface);color:var(--re-data-grid-loading-color);z-index:5}.re-dg-scrollbar{position:absolute;right:0;top:0;bottom:0;opacity:0;transition:opacity .15s ease-in-out;pointer-events:none;z-index:4}.re-dg-scrollbar.visible{opacity:1}.re-dg-scrollbar-thumb{position:absolute;right:var(--re-data-grid-scrollbar-offset);width:var(--re-data-grid-scrollbar-thumb-size);border-radius:var(--re-data-grid-scrollbar-thumb-rounded);background:var(--re-data-grid-scrollbar-thumb-color);pointer-events:auto;-webkit-user-select:none;user-select:none}.re-dg-spacer{width:1px}.re-dg-top{top:0}.re-dg-bottom{bottom:0}\n"] }]
|
|
1827
|
+
}], ctorParameters: () => [], propDecorators: { data: [{ type: i0.Input, args: [{ isSignal: true, alias: "data", required: false }] }], columns: [{ type: i0.Input, args: [{ isSignal: true, alias: "columns", required: false }] }], mode: [{ type: i0.Input, args: [{ isSignal: true, alias: "mode", required: false }] }], pinnedRows: [{ type: i0.Input, args: [{ isSignal: true, alias: "pinnedRows", required: false }] }], hasIndexColumn: [{ type: i0.Input, args: [{ isSignal: true, alias: "hasIndexColumn", required: false }] }], selection: [{ type: i0.Input, args: [{ isSignal: true, alias: "selection", required: false }] }], pageSize: [{ type: i0.Input, args: [{ isSignal: true, alias: "pageSize", required: false }] }], rowHeight: [{ type: i0.Input, args: [{ isSignal: true, alias: "rowHeight", required: false }] }], height: [{ type: i0.Input, args: [{ isSignal: true, alias: "height", required: false }] }], virtualBuffer: [{ type: i0.Input, args: [{ isSignal: true, alias: "virtualBuffer", required: false }] }], loading: [{ type: i0.Input, args: [{ isSignal: true, alias: "loading", required: false }] }], rowKey: [{ type: i0.Input, args: [{ isSignal: true, alias: "rowKey", required: false }] }], pageStartFromZero: [{ type: i0.Input, args: [{ isSignal: true, alias: "pageStartFromZero", required: false }] }], pageChange: [{ type: i0.Output, args: ["pageChange"] }], sortChange: [{ type: i0.Output, args: ["sortChange"] }], selectChange: [{ type: i0.Output, args: ["selectChange"] }], rowClick: [{ type: i0.Output, args: ["rowClick"] }], cellClick: [{ type: i0.Output, args: ["cellClick"] }], rootEl: [{ type: i0.ViewChild, args: ['root', { isSignal: true }] }], scrollEl: [{ type: i0.ViewChild, args: ['scroll', { isSignal: true }] }], headerEl: [{ type: i0.ViewChild, args: ['header', { isSignal: true }] }], cellTypedSlotRefs: [{ type: i0.ContentChildren, args: [i0.forwardRef(() => DataGridTypeCellTemplateDirective), { isSignal: true }] }], cellDataSlotRefs: [{ type: i0.ContentChildren, args: [i0.forwardRef(() => DataGridCellTemplateDirective), { isSignal: true }] }], headerSlotRefs: [{ type: i0.ContentChildren, args: [i0.forwardRef(() => DataGridHeaderTemplateDirective), { isSignal: true }] }], emptySlotRefs: [{ type: i0.ContentChildren, args: [i0.forwardRef(() => DataGridCellEmptyDirective), { isSignal: true }] }], loadingSlotRefs: [{ type: i0.ContentChildren, args: [i0.forwardRef(() => DataGridCellLoadingDirective), { isSignal: true }] }], sortIcSlotRefs: [{ type: i0.ContentChildren, args: [i0.forwardRef(() => DataGridSortIconDirective), { isSignal: true }] }], expanderIcSlotRefs: [{ type: i0.ContentChildren, args: [i0.forwardRef(() => DataGridExpanderIconDirective), { isSignal: true }] }] } });
|
|
944
1828
|
|
|
945
1829
|
/**
|
|
946
1830
|
* Generated bundle index. Do not edit.
|
|
947
1831
|
*/
|
|
948
1832
|
|
|
949
|
-
export { DataGrid, DataGridCellEmptyDirective, DataGridCellLoadingDirective, DataGridHeaderTemplateDirective, DataGridTypeCellTemplateDirective };
|
|
1833
|
+
export { DataGrid, DataGridCellEmptyDirective, DataGridCellLoadingDirective, DataGridCellTemplateDirective, DataGridExpanderIconDirective, DataGridHeaderTemplateDirective, DataGridPaginator, DataGridSortIconDirective, DataGridTypeCellTemplateDirective };
|
|
950
1834
|
//# sourceMappingURL=reforgium-data-grid.mjs.map
|