@angular-generic-table/core 5.0.0-rc.2 → 5.0.0-rc.20
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/esm2020/angular-generic-table-core.mjs +5 -0
- package/esm2020/lib/core.component.mjs +560 -0
- package/esm2020/lib/core.module.mjs +47 -0
- package/esm2020/lib/core.service.mjs +14 -0
- package/esm2020/lib/gt-delta/gt-delta.component.mjs +126 -0
- package/esm2020/lib/models/gt-pagination.mjs +2 -0
- package/esm2020/lib/models/table-column.interface.mjs +2 -0
- package/esm2020/lib/models/table-config.interface.mjs +2 -0
- package/esm2020/lib/models/table-events.interface.mjs +2 -0
- package/esm2020/lib/models/table-info.interface.mjs +2 -0
- package/esm2020/lib/models/table-meta.interface.mjs +2 -0
- package/esm2020/lib/models/table-row.interface.mjs +2 -0
- package/esm2020/lib/models/table-sort.interface.mjs +2 -0
- package/esm2020/lib/pagination/pagination.component.mjs +157 -0
- package/esm2020/lib/pagination/pagination.module.mjs +17 -0
- package/esm2020/lib/pipes/capital-case.pipe.mjs +18 -0
- package/esm2020/lib/pipes/dash-case.pipe.mjs +18 -0
- package/esm2020/lib/pipes/dynamic.pipe.mjs +26 -0
- package/esm2020/lib/pipes/highlight.pipe.mjs +49 -0
- package/esm2020/lib/pipes/row-selection.pipe.mjs +17 -0
- package/esm2020/lib/pipes/sort-class.pipe.mjs +40 -0
- package/esm2020/lib/utilities/utilities.mjs +186 -0
- package/esm2020/public-api.mjs +24 -0
- package/fesm2015/angular-generic-table-core.mjs +1253 -0
- package/fesm2015/angular-generic-table-core.mjs.map +1 -0
- package/fesm2020/angular-generic-table-core.mjs +1240 -0
- package/fesm2020/angular-generic-table-core.mjs.map +1 -0
- package/index.d.ts +5 -0
- package/lib/core.component.d.ts +123 -18
- package/lib/core.module.d.ts +13 -0
- package/lib/core.service.d.ts +3 -0
- package/lib/gt-delta/gt-delta.component.d.ts +32 -0
- package/lib/models/gt-pagination.d.ts +18 -0
- package/lib/models/table-column.interface.d.ts +15 -3
- package/lib/models/table-config.interface.d.ts +44 -6
- package/lib/models/table-events.interface.d.ts +27 -0
- package/lib/models/table-info.interface.d.ts +3 -1
- package/lib/models/table-meta.interface.d.ts +8 -0
- package/lib/models/table-row.interface.d.ts +1 -1
- package/lib/models/table-sort.interface.d.ts +6 -4
- package/lib/pagination/pagination.component.d.ts +62 -0
- package/lib/pagination/pagination.module.d.ts +8 -0
- package/lib/pipes/capital-case.pipe.d.ts +7 -0
- package/lib/pipes/dash-case.pipe.d.ts +3 -0
- package/lib/pipes/dynamic.pipe.d.ts +9 -0
- package/lib/pipes/highlight.pipe.d.ts +3 -0
- package/lib/pipes/row-selection.pipe.d.ts +8 -0
- package/lib/pipes/sort-class.pipe.d.ts +5 -5
- package/lib/utilities/utilities.d.ts +32 -0
- package/package.json +22 -10
- package/public-api.d.ts +12 -0
- package/scss/index.scss +319 -0
- package/angular-generic-table-core.d.ts +0 -7
- package/angular-generic-table-core.metadata.json +0 -1
- package/bundles/angular-generic-table-core.umd.js +0 -594
- package/bundles/angular-generic-table-core.umd.js.map +0 -1
- package/bundles/angular-generic-table-core.umd.min.js +0 -2
- package/bundles/angular-generic-table-core.umd.min.js.map +0 -1
- package/esm2015/angular-generic-table-core.js +0 -8
- package/esm2015/lib/core.component.js +0 -107
- package/esm2015/lib/core.module.js +0 -16
- package/esm2015/lib/core.service.js +0 -13
- package/esm2015/lib/enums/order.enum.js +0 -6
- package/esm2015/lib/models/table-column.interface.js +0 -2
- package/esm2015/lib/models/table-config.interface.js +0 -2
- package/esm2015/lib/models/table-info.interface.js +0 -2
- package/esm2015/lib/models/table-row.interface.js +0 -2
- package/esm2015/lib/models/table-sort.interface.js +0 -2
- package/esm2015/lib/pipes/dash-case.pipe.js +0 -13
- package/esm2015/lib/pipes/highlight.pipe.js +0 -44
- package/esm2015/lib/pipes/sort-class.pipe.js +0 -12
- package/esm2015/lib/utilities/utilities.js +0 -25
- package/esm2015/public-api.js +0 -12
- package/fesm2015/angular-generic-table-core.js +0 -234
- package/fesm2015/angular-generic-table-core.js.map +0 -1
- package/lib/enums/order.enum.d.ts +0 -4
|
@@ -0,0 +1,1240 @@
|
|
|
1
|
+
import * as i0 from '@angular/core';
|
|
2
|
+
import { Injectable, Pipe, Injector, EventEmitter, Component, ChangeDetectionStrategy, Input, Output, NgModule } from '@angular/core';
|
|
3
|
+
import { Subject, ReplaySubject, BehaviorSubject, isObservable, of, combineLatest, fromEvent } from 'rxjs';
|
|
4
|
+
import * as i1 from '@angular/common';
|
|
5
|
+
import { KeyValuePipe, AsyncPipe, NgTemplateOutlet, SlicePipe, NgClass, NgIf, NgForOf, PercentPipe, CommonModule } from '@angular/common';
|
|
6
|
+
import { distinctUntilChanged, tap, shareReplay, startWith, map, switchMap, withLatestFrom, filter, pluck, take, takeUntil } from 'rxjs/operators';
|
|
7
|
+
|
|
8
|
+
class CoreService {
|
|
9
|
+
constructor() { }
|
|
10
|
+
}
|
|
11
|
+
CoreService.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.1.1", ngImport: i0, type: CoreService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
12
|
+
CoreService.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "15.1.1", ngImport: i0, type: CoreService, providedIn: 'root' });
|
|
13
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.1.1", ngImport: i0, type: CoreService, decorators: [{
|
|
14
|
+
type: Injectable,
|
|
15
|
+
args: [{
|
|
16
|
+
providedIn: 'root',
|
|
17
|
+
}]
|
|
18
|
+
}], ctorParameters: function () { return []; } });
|
|
19
|
+
|
|
20
|
+
let dashed;
|
|
21
|
+
dashed = (s) => s.replace(/[A-Z]/g, (m) => '-' + m.toLowerCase());
|
|
22
|
+
let capitalize;
|
|
23
|
+
capitalize = (s) => (s.charAt(0).toUpperCase() + s.slice(1))
|
|
24
|
+
.replace(/_/g, ' ')
|
|
25
|
+
.replace(/([A-Z][a-z]+)/g, ' $1')
|
|
26
|
+
.replace(/([A-Z]{2,})/g, ' $1')
|
|
27
|
+
.replace(/\s{2,}/g, ' ')
|
|
28
|
+
.trim();
|
|
29
|
+
let chunk;
|
|
30
|
+
chunk = (array, chunkSize) => {
|
|
31
|
+
if (chunkSize < 0) {
|
|
32
|
+
throw new Error('Invalid chunk size');
|
|
33
|
+
}
|
|
34
|
+
if (chunkSize === 0 && !chunkSize) {
|
|
35
|
+
return [array];
|
|
36
|
+
}
|
|
37
|
+
const CHUNK = [];
|
|
38
|
+
for (let i = 0, len = array.length; i < len; i += chunkSize) {
|
|
39
|
+
CHUNK.push(array.slice(i, i + chunkSize));
|
|
40
|
+
}
|
|
41
|
+
return CHUNK;
|
|
42
|
+
};
|
|
43
|
+
let search;
|
|
44
|
+
search = (text, caseSensitive, data, config) => {
|
|
45
|
+
if (config.columns) {
|
|
46
|
+
const searchColumns = Object.keys(config.columns).filter(
|
|
47
|
+
// @ts-ignore
|
|
48
|
+
(key) => config.columns &&
|
|
49
|
+
!config.columns[key]?.hidden &&
|
|
50
|
+
config.columns[key]?.search !== false);
|
|
51
|
+
const FILTERED = [];
|
|
52
|
+
for (let i = 0; i < data.length; i++) {
|
|
53
|
+
const row = data[i];
|
|
54
|
+
const match = Object.entries(row)
|
|
55
|
+
.filter(([key, value]) => searchColumns.indexOf(key) !== -1)
|
|
56
|
+
.reduce((acc, [key, value], index) => {
|
|
57
|
+
const search = config?.columns[key]?.search;
|
|
58
|
+
// if search is a function...
|
|
59
|
+
if (typeof search === 'function') {
|
|
60
|
+
// ...use search function to return value to search
|
|
61
|
+
value = search(row, key, value);
|
|
62
|
+
}
|
|
63
|
+
return (acc +
|
|
64
|
+
(index === 0 ? '' : ' ? ') +
|
|
65
|
+
(caseSensitive ? value + '' : (value + '').toLowerCase()));
|
|
66
|
+
}, '')
|
|
67
|
+
.indexOf(caseSensitive ? text : text.toLowerCase()) !== -1;
|
|
68
|
+
if (match) {
|
|
69
|
+
FILTERED[FILTERED.length] = row;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
return FILTERED;
|
|
73
|
+
}
|
|
74
|
+
else {
|
|
75
|
+
return data;
|
|
76
|
+
}
|
|
77
|
+
};
|
|
78
|
+
let calculate = (data, config) => {
|
|
79
|
+
const CALCULATIONS = {};
|
|
80
|
+
const COLUMN_CALCULATIONS = {};
|
|
81
|
+
let CALCULATED = Object.entries(config.footer?.columns || {})
|
|
82
|
+
.filter(([columnName, calculations]) => Object.values(calculations || {}).filter((value) => value !== false)
|
|
83
|
+
.length >= 0)
|
|
84
|
+
.reduce((acc, [columnName, calculations], index) => {
|
|
85
|
+
acc[columnName] = Object.entries(calculations || {})
|
|
86
|
+
.filter(([calculation, value]) => value !== false)
|
|
87
|
+
.reduce((acc, [calculation, value]) => {
|
|
88
|
+
if (COLUMN_CALCULATIONS[columnName]) {
|
|
89
|
+
COLUMN_CALCULATIONS[columnName].push(calculation);
|
|
90
|
+
}
|
|
91
|
+
else if (value === true) {
|
|
92
|
+
COLUMN_CALCULATIONS[columnName] = [calculation];
|
|
93
|
+
}
|
|
94
|
+
CALCULATIONS[calculation] = true;
|
|
95
|
+
if (typeof value === 'function') {
|
|
96
|
+
value = value(data, columnName);
|
|
97
|
+
}
|
|
98
|
+
acc[calculation] = value === true ? 0 : value;
|
|
99
|
+
return acc;
|
|
100
|
+
}, {});
|
|
101
|
+
return acc;
|
|
102
|
+
}, {});
|
|
103
|
+
if (Object.keys(CALCULATED).length > 0) {
|
|
104
|
+
for (let i = 0; i < data.length; i++) {
|
|
105
|
+
Object.entries(COLUMN_CALCULATIONS).forEach(([column, calculations]) => {
|
|
106
|
+
if (calculations.indexOf('sum') > -1) {
|
|
107
|
+
CALCULATED[column].sum += data[i][column];
|
|
108
|
+
}
|
|
109
|
+
if (calculations.indexOf('avg') > -1 &&
|
|
110
|
+
calculations.indexOf('sum') === -1) {
|
|
111
|
+
if (CALCULATED[column].sum === undefined) {
|
|
112
|
+
CALCULATED[column].sum = 0;
|
|
113
|
+
}
|
|
114
|
+
CALCULATED[column].sum += data[i][column];
|
|
115
|
+
}
|
|
116
|
+
if (calculations.indexOf('max') > -1 &&
|
|
117
|
+
(!CALCULATED[column].max || +data[i][column] > CALCULATED[column].max)) {
|
|
118
|
+
CALCULATED[column].max = +data[i][column];
|
|
119
|
+
}
|
|
120
|
+
if (calculations.indexOf('min') > -1 &&
|
|
121
|
+
(!CALCULATED[column].min || +data[i][column] < CALCULATED[column].min)) {
|
|
122
|
+
CALCULATED[column].min = +data[i][column];
|
|
123
|
+
}
|
|
124
|
+
});
|
|
125
|
+
}
|
|
126
|
+
Object.entries(COLUMN_CALCULATIONS).forEach(([column, calculations]) => {
|
|
127
|
+
if (calculations.indexOf('avg') !== -1) {
|
|
128
|
+
CALCULATED[column].avg = CALCULATED[column]?.sum / data.length;
|
|
129
|
+
// if sum is not part of calculations config...
|
|
130
|
+
if (calculations.indexOf('sum') === -1 && CALCULATED[column].sum) {
|
|
131
|
+
// ...remove it
|
|
132
|
+
delete CALCULATED[column].sum;
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
if (calculations.indexOf('count') !== -1) {
|
|
136
|
+
CALCULATED[column].count = data.length;
|
|
137
|
+
}
|
|
138
|
+
});
|
|
139
|
+
}
|
|
140
|
+
return {
|
|
141
|
+
calculated: CALCULATED,
|
|
142
|
+
calculations: Object.keys(CALCULATIONS).sort((a, b) => (config.footer?.rowOrder?.indexOf(a) || 0) -
|
|
143
|
+
(config.footer?.rowOrder?.indexOf(b) || 0)),
|
|
144
|
+
calculatedColumnsCount: Object.keys(CALCULATED).length || 0,
|
|
145
|
+
};
|
|
146
|
+
};
|
|
147
|
+
/** sortOnMultipleKeys
|
|
148
|
+
* @description Sort data on multiple keys
|
|
149
|
+
* @param {GtSortOrder} keys - array with sort config objects to sort on, data will be sorted according to array order
|
|
150
|
+
* @returns sort function
|
|
151
|
+
*/
|
|
152
|
+
const sortOnMultipleKeys = (keys) => {
|
|
153
|
+
const order = keys.map((key) => (key.order === 'desc' ? -1 : 1));
|
|
154
|
+
return (a, b) => {
|
|
155
|
+
for (let i = 0; i < keys.length; i++) {
|
|
156
|
+
const o = keys[i].key;
|
|
157
|
+
if (a[o] === b[o])
|
|
158
|
+
return 0;
|
|
159
|
+
if (a[o] === null)
|
|
160
|
+
return 1;
|
|
161
|
+
if (b[o] === null)
|
|
162
|
+
return -1;
|
|
163
|
+
if (a[o] > b[o])
|
|
164
|
+
return order[i];
|
|
165
|
+
if (a[o] < b[o])
|
|
166
|
+
return -order[i];
|
|
167
|
+
}
|
|
168
|
+
return 0;
|
|
169
|
+
};
|
|
170
|
+
};
|
|
171
|
+
/** parseSortOrderParams
|
|
172
|
+
* @description Convert sort order query param to array with sort config objects
|
|
173
|
+
* @param sortParams - Query param string where each sort config object is separated by comma and order is indicated by + (ascending) or - (descending), e.g. _'name,-age'_
|
|
174
|
+
* @returns GtSortOrder - Array with sort config objects
|
|
175
|
+
*/
|
|
176
|
+
const parseSortOrderParams = (sortParams) => {
|
|
177
|
+
const sortParamsArray = sortParams.split(',');
|
|
178
|
+
return sortParamsArray.map((sortParam) => {
|
|
179
|
+
const [key, order] = sortParam.split(':');
|
|
180
|
+
return {
|
|
181
|
+
key: key.replace(/^[+-]/, ''),
|
|
182
|
+
order: order === 'desc' ? 'desc' : 'asc',
|
|
183
|
+
};
|
|
184
|
+
});
|
|
185
|
+
};
|
|
186
|
+
/** sortOrderConfigToParam
|
|
187
|
+
* @description Convert sort config object to string that can be used as query param when sorting is implemented server side
|
|
188
|
+
* @param sortConfig - Sort config object
|
|
189
|
+
* @returns string - Query param string where order is indicated by + (ascending) or - (descending), e.g. _'-name'_
|
|
190
|
+
*/
|
|
191
|
+
const sortOrderConfigToParam = (sortConfig) => {
|
|
192
|
+
const order = sortConfig.order === 'desc' ? '-' : '+';
|
|
193
|
+
return `${order}${sortConfig.key}`;
|
|
194
|
+
};
|
|
195
|
+
/** sortOrderToParams
|
|
196
|
+
* @description Convert sort order array to string that can be used as query param when sorting is implemented server side
|
|
197
|
+
* @param sortOrder - Array with sort config objects
|
|
198
|
+
* @returns string - Query param string where each sort config object is separated by comma and order is indicated by + (ascending) or - (descending), e.g. _'name,-age'_
|
|
199
|
+
*/
|
|
200
|
+
const sortOrderToParams = (sortOrder) => {
|
|
201
|
+
return sortOrder
|
|
202
|
+
.map((sortConfig) => sortOrderConfigToParam(sortConfig))
|
|
203
|
+
.join(',');
|
|
204
|
+
};
|
|
205
|
+
|
|
206
|
+
class CapitalCasePipe {
|
|
207
|
+
transform(s) {
|
|
208
|
+
return capitalize(s);
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
CapitalCasePipe.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.1.1", ngImport: i0, type: CapitalCasePipe, deps: [], target: i0.ɵɵFactoryTarget.Pipe });
|
|
212
|
+
CapitalCasePipe.ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "15.1.1", ngImport: i0, type: CapitalCasePipe, isStandalone: true, name: "capitalCase" });
|
|
213
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.1.1", ngImport: i0, type: CapitalCasePipe, decorators: [{
|
|
214
|
+
type: Pipe,
|
|
215
|
+
args: [{
|
|
216
|
+
name: 'capitalCase',
|
|
217
|
+
standalone: true,
|
|
218
|
+
}]
|
|
219
|
+
}] });
|
|
220
|
+
|
|
221
|
+
class SortClassPipe {
|
|
222
|
+
transform(sortOrder, key, context = 'class') {
|
|
223
|
+
const sortIndex = sortOrder
|
|
224
|
+
? sortOrder.findIndex((s) => s.key === key)
|
|
225
|
+
: -1;
|
|
226
|
+
if (context === 'aria') {
|
|
227
|
+
if (sortIndex === -1 || !sortOrder) {
|
|
228
|
+
return null;
|
|
229
|
+
}
|
|
230
|
+
else {
|
|
231
|
+
return `${sortOrder[sortIndex].order}ending`;
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
else if (context === 'class') {
|
|
235
|
+
if (sortIndex === -1 || !sortOrder) {
|
|
236
|
+
return '';
|
|
237
|
+
}
|
|
238
|
+
else {
|
|
239
|
+
return `gt-sort-${sortOrder[sortIndex].order}`;
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
else {
|
|
243
|
+
return (sortOrder && sortOrder?.length === 1) || sortIndex < 0
|
|
244
|
+
? null
|
|
245
|
+
: sortIndex + 1 + '';
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
SortClassPipe.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.1.1", ngImport: i0, type: SortClassPipe, deps: [], target: i0.ɵɵFactoryTarget.Pipe });
|
|
250
|
+
SortClassPipe.ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "15.1.1", ngImport: i0, type: SortClassPipe, isStandalone: true, name: "sortClass" });
|
|
251
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.1.1", ngImport: i0, type: SortClassPipe, decorators: [{
|
|
252
|
+
type: Pipe,
|
|
253
|
+
args: [{
|
|
254
|
+
name: 'sortClass',
|
|
255
|
+
standalone: true,
|
|
256
|
+
}]
|
|
257
|
+
}] });
|
|
258
|
+
|
|
259
|
+
class DashCasePipe {
|
|
260
|
+
transform(s) {
|
|
261
|
+
return dashed(s);
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
DashCasePipe.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.1.1", ngImport: i0, type: DashCasePipe, deps: [], target: i0.ɵɵFactoryTarget.Pipe });
|
|
265
|
+
DashCasePipe.ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "15.1.1", ngImport: i0, type: DashCasePipe, isStandalone: true, name: "dashCase" });
|
|
266
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.1.1", ngImport: i0, type: DashCasePipe, decorators: [{
|
|
267
|
+
type: Pipe,
|
|
268
|
+
args: [{
|
|
269
|
+
name: 'dashCase',
|
|
270
|
+
standalone: true,
|
|
271
|
+
}]
|
|
272
|
+
}] });
|
|
273
|
+
|
|
274
|
+
class DynamicPipe {
|
|
275
|
+
constructor(injector) {
|
|
276
|
+
this.injector = injector;
|
|
277
|
+
}
|
|
278
|
+
transform(value, requiredPipe, pipeArgs) {
|
|
279
|
+
const injector = Injector.create({
|
|
280
|
+
name: 'DynamicPipe',
|
|
281
|
+
parent: this.injector,
|
|
282
|
+
providers: [{ provide: requiredPipe }],
|
|
283
|
+
});
|
|
284
|
+
const pipe = injector.get(requiredPipe);
|
|
285
|
+
return pipe.transform(value, ...(pipeArgs || []));
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
DynamicPipe.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.1.1", ngImport: i0, type: DynamicPipe, deps: [{ token: i0.Injector }], target: i0.ɵɵFactoryTarget.Pipe });
|
|
289
|
+
DynamicPipe.ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "15.1.1", ngImport: i0, type: DynamicPipe, isStandalone: true, name: "dynamicPipe" });
|
|
290
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.1.1", ngImport: i0, type: DynamicPipe, decorators: [{
|
|
291
|
+
type: Pipe,
|
|
292
|
+
args: [{
|
|
293
|
+
name: 'dynamicPipe',
|
|
294
|
+
standalone: true,
|
|
295
|
+
}]
|
|
296
|
+
}], ctorParameters: function () { return [{ type: i0.Injector }]; } });
|
|
297
|
+
|
|
298
|
+
class HighlightPipe {
|
|
299
|
+
transform(text, searchTerm) {
|
|
300
|
+
if (!searchTerm) {
|
|
301
|
+
return text;
|
|
302
|
+
}
|
|
303
|
+
const haystackAlwaysString = text + '';
|
|
304
|
+
let highlightedText = haystackAlwaysString; // fallback
|
|
305
|
+
let searchPattern;
|
|
306
|
+
try {
|
|
307
|
+
searchPattern = new RegExp('(' +
|
|
308
|
+
// @ts-ignore
|
|
309
|
+
searchTerm
|
|
310
|
+
.toLowerCase()
|
|
311
|
+
.match(/".*?"|[^ ]+/g) // extract words
|
|
312
|
+
.map((needle) => needle.replace(/"(.*?)"/, '$1') // strip away '"'
|
|
313
|
+
)
|
|
314
|
+
.join('|') + // combine words
|
|
315
|
+
')', 'ig');
|
|
316
|
+
}
|
|
317
|
+
catch (error) {
|
|
318
|
+
return highlightedText;
|
|
319
|
+
}
|
|
320
|
+
const containsTagPattern = /(<.*?>)(.*)(<\/.*?>)/gi;
|
|
321
|
+
const containsTagMatches = containsTagPattern.exec(haystackAlwaysString);
|
|
322
|
+
if (containsTagMatches) {
|
|
323
|
+
// tag exists in haystack
|
|
324
|
+
highlightedText =
|
|
325
|
+
containsTagMatches[1] +
|
|
326
|
+
containsTagMatches[2].replace(searchPattern, '<span class="gt-highlight-search">$1</span>') +
|
|
327
|
+
containsTagMatches[3];
|
|
328
|
+
}
|
|
329
|
+
else {
|
|
330
|
+
highlightedText = haystackAlwaysString.replace(searchPattern, '<span class="gt-highlight-search">$1</span>');
|
|
331
|
+
}
|
|
332
|
+
return highlightedText;
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
HighlightPipe.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.1.1", ngImport: i0, type: HighlightPipe, deps: [], target: i0.ɵɵFactoryTarget.Pipe });
|
|
336
|
+
HighlightPipe.ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "15.1.1", ngImport: i0, type: HighlightPipe, isStandalone: true, name: "highlight" });
|
|
337
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.1.1", ngImport: i0, type: HighlightPipe, decorators: [{
|
|
338
|
+
type: Pipe,
|
|
339
|
+
args: [{
|
|
340
|
+
name: 'highlight',
|
|
341
|
+
standalone: true,
|
|
342
|
+
}]
|
|
343
|
+
}] });
|
|
344
|
+
|
|
345
|
+
class RowSelectionPipe {
|
|
346
|
+
transform(row, selection, comparator, className) {
|
|
347
|
+
return className && comparator(row, selection) ? className : '';
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
RowSelectionPipe.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.1.1", ngImport: i0, type: RowSelectionPipe, deps: [], target: i0.ɵɵFactoryTarget.Pipe });
|
|
351
|
+
RowSelectionPipe.ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "15.1.1", ngImport: i0, type: RowSelectionPipe, isStandalone: true, name: "rowSelection" });
|
|
352
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.1.1", ngImport: i0, type: RowSelectionPipe, decorators: [{
|
|
353
|
+
type: Pipe,
|
|
354
|
+
args: [{
|
|
355
|
+
name: 'rowSelection',
|
|
356
|
+
standalone: true,
|
|
357
|
+
}]
|
|
358
|
+
}] });
|
|
359
|
+
|
|
360
|
+
class CoreComponent {
|
|
361
|
+
constructor() {
|
|
362
|
+
this.unsubscribe$ = new Subject();
|
|
363
|
+
this._navigationKeys = [
|
|
364
|
+
'ArrowDown',
|
|
365
|
+
'ArrowUp',
|
|
366
|
+
'ArrowLeft',
|
|
367
|
+
'ArrowRight',
|
|
368
|
+
'Home',
|
|
369
|
+
'End',
|
|
370
|
+
];
|
|
371
|
+
this._selectKeys = ['Enter', ' '];
|
|
372
|
+
this._customClasses = {
|
|
373
|
+
selectedRow: 'gt-selected',
|
|
374
|
+
activeRow: 'gt-active',
|
|
375
|
+
};
|
|
376
|
+
/** selection
|
|
377
|
+
* @description An object that contains the currently selected row(s) in the table. It's passed to the selection function to determine which rows should be selected.
|
|
378
|
+
* @type {any}
|
|
379
|
+
*/
|
|
380
|
+
this.selection = {};
|
|
381
|
+
/** generateRowId
|
|
382
|
+
* @description Whether or not to generate a unique id for each row in the table. Defaults to `true`.
|
|
383
|
+
* @type {boolean}
|
|
384
|
+
*/
|
|
385
|
+
this.generateRowId = true;
|
|
386
|
+
this.rowClick = new EventEmitter();
|
|
387
|
+
this.rowSelect = new EventEmitter();
|
|
388
|
+
this.sortOrderChange = new EventEmitter();
|
|
389
|
+
this._rowActive$ = new ReplaySubject(1);
|
|
390
|
+
this.rowActive = new EventEmitter();
|
|
391
|
+
this.columnSort = new EventEmitter();
|
|
392
|
+
/** page change event - emitted when current page/index changes for pagination */
|
|
393
|
+
this.pageChange = new EventEmitter();
|
|
394
|
+
this.rowActive$ = this._rowActive$.asObservable().pipe(distinctUntilChanged((p, q) => {
|
|
395
|
+
if (this.rowIdKey && p.row && q.row) {
|
|
396
|
+
return p.row[this.rowIdKey] === q.row[this.rowIdKey];
|
|
397
|
+
}
|
|
398
|
+
else if (this.generateRowId && p.row && q.row) {
|
|
399
|
+
return p.row._id === q.row._id;
|
|
400
|
+
}
|
|
401
|
+
else {
|
|
402
|
+
return p.index === q.index;
|
|
403
|
+
}
|
|
404
|
+
}), tap((event) => (this.activeRowIndex = event.index)), tap((event) => this.rowActive.emit(event)), shareReplay(1));
|
|
405
|
+
this.activeRowIndex = null;
|
|
406
|
+
this._loading$ = new ReplaySubject(1);
|
|
407
|
+
this._sortOrder$ = new BehaviorSubject([]);
|
|
408
|
+
this._searchBy$ = new ReplaySubject(1);
|
|
409
|
+
this.searchBy$ = this._searchBy$.pipe(startWith(''), map((value) => (isObservable(value) ? value : of(value))), switchMap((obs) => obs), shareReplay(1));
|
|
410
|
+
this._pagingInfo$ = new BehaviorSubject({
|
|
411
|
+
pageCurrent: null,
|
|
412
|
+
pageNext: null,
|
|
413
|
+
pagePrevious: null,
|
|
414
|
+
pageSize: null,
|
|
415
|
+
numberOfRecords: null,
|
|
416
|
+
//recordsAfterFilter: null,
|
|
417
|
+
//recordsAfterSearch: null,
|
|
418
|
+
//recordsAll: null,
|
|
419
|
+
});
|
|
420
|
+
// tslint:disable-next-line:variable-name
|
|
421
|
+
this._tableConfig$ = new BehaviorSubject({});
|
|
422
|
+
this.tableConfig$ = this._tableConfig$.pipe(map((value) => (isObservable(value) ? value : of(value))), switchMap((obs) => obs), tap((config) => (this._tableConfig = config)), shareReplay(1));
|
|
423
|
+
this._data$ = new ReplaySubject(1);
|
|
424
|
+
this.data$ = this._data$.pipe(map((value) => (isObservable(value) ? value : of(value))), switchMap((obs) => combineLatest([obs])), withLatestFrom(this.tableConfig$), map(([[data], config]) => {
|
|
425
|
+
// if columns or rows contains config for mapTo...
|
|
426
|
+
if ((config.columns &&
|
|
427
|
+
!!Object.values(config.columns).find((column) => !!column.mapTo)) ||
|
|
428
|
+
(config.rows &&
|
|
429
|
+
!!Object.values(config.rows).find((column) => !!column.mapTo))) {
|
|
430
|
+
// ...map data to new keys on row...
|
|
431
|
+
const newData = [];
|
|
432
|
+
for (let i = 0; i < data.length; i++) {
|
|
433
|
+
const row = data[i];
|
|
434
|
+
const newKeys = Object.entries(config.columns || config.rows || [])
|
|
435
|
+
.filter(([key, value]) => !!value.mapTo) // add keys for columns with mapTo config...
|
|
436
|
+
.reduce((previousValue, [key, value]) => ({
|
|
437
|
+
...previousValue,
|
|
438
|
+
[key]: this.nestedValue(row, value.mapTo.path, value.mapTo?.missingValue),
|
|
439
|
+
}), {});
|
|
440
|
+
newData[i] = { ...row, ...newKeys };
|
|
441
|
+
}
|
|
442
|
+
data = newData;
|
|
443
|
+
}
|
|
444
|
+
if (this.generateRowId && !this.rowIdKey && data.length > 0) {
|
|
445
|
+
const dataWithId = [];
|
|
446
|
+
for (let i = 0; i < data.length; i++) {
|
|
447
|
+
dataWithId[i] = { ...data[i], _id: i };
|
|
448
|
+
}
|
|
449
|
+
data = dataWithId;
|
|
450
|
+
}
|
|
451
|
+
return { data, config };
|
|
452
|
+
}), switchMap((obs) => combineLatest([of(obs), this.sortOrder$, this.searchBy$])), map(([table, sortBy, searchBy]) => {
|
|
453
|
+
// create a new array reference and sort new array (prevent mutating existing state)
|
|
454
|
+
table.data = [...table.data];
|
|
455
|
+
return !sortBy?.length || table.config?.disableTableSort
|
|
456
|
+
? searchBy && !this.tableInfo?.lazyLoaded
|
|
457
|
+
? search(searchBy, false, table.data, table.config)
|
|
458
|
+
: table.data
|
|
459
|
+
: searchBy && !this.tableInfo?.lazyLoaded
|
|
460
|
+
? search(searchBy, false, table.data, table.config)?.sort(sortOnMultipleKeys(sortBy))
|
|
461
|
+
: table.data?.sort(sortOnMultipleKeys(sortBy));
|
|
462
|
+
}), shareReplay(1));
|
|
463
|
+
this.calculations$ = combineLatest([this.data$, this.tableConfig$]).pipe(map(([data, config]) => calculate(data, config)), shareReplay(1));
|
|
464
|
+
this.table$ = combineLatest([
|
|
465
|
+
this.data$,
|
|
466
|
+
this.tableConfig$,
|
|
467
|
+
this._pagingInfo$,
|
|
468
|
+
]).pipe(map(([sorted, config, pagingInfo]) => {
|
|
469
|
+
if (pagingInfo.pageCurrent !== null &&
|
|
470
|
+
pagingInfo.numberOfRecords !== null &&
|
|
471
|
+
pagingInfo.pageSize !== null) {
|
|
472
|
+
return {
|
|
473
|
+
data: [sorted],
|
|
474
|
+
config,
|
|
475
|
+
info: {
|
|
476
|
+
lazyLoaded: true,
|
|
477
|
+
numberOfRecords: pagingInfo.numberOfRecords,
|
|
478
|
+
pageSize: pagingInfo.pageSize,
|
|
479
|
+
pageTotal: pagingInfo.pageTotal ??
|
|
480
|
+
Math.ceil(pagingInfo.numberOfRecords / pagingInfo.pageSize),
|
|
481
|
+
},
|
|
482
|
+
};
|
|
483
|
+
}
|
|
484
|
+
// if pagination is disabled...
|
|
485
|
+
if (!config.pagination || config.pagination.length === 0) {
|
|
486
|
+
// ...return unaltered array
|
|
487
|
+
return {
|
|
488
|
+
data: [sorted],
|
|
489
|
+
config,
|
|
490
|
+
info: { numberOfRecords: sorted.length, pageTotal: 1 },
|
|
491
|
+
};
|
|
492
|
+
}
|
|
493
|
+
// return record set
|
|
494
|
+
return {
|
|
495
|
+
data: chunk(sorted, +(config.pagination.length || 0)),
|
|
496
|
+
config,
|
|
497
|
+
info: {
|
|
498
|
+
numberOfRecords: sorted.length,
|
|
499
|
+
pageSize: +(config.pagination.length || 0),
|
|
500
|
+
pageTotal: Math.ceil(sorted.length / +(config.pagination.length || 0)),
|
|
501
|
+
},
|
|
502
|
+
};
|
|
503
|
+
}), tap((meta) => this._tableInfo$.next(meta.info)), shareReplay(1));
|
|
504
|
+
this._tableInfo$ = new BehaviorSubject(undefined);
|
|
505
|
+
this._currentPaginationIndex$ = new BehaviorSubject(0);
|
|
506
|
+
this.currentPaginationIndex$ = combineLatest([
|
|
507
|
+
this._currentPaginationIndex$,
|
|
508
|
+
this.table$,
|
|
509
|
+
]).pipe(map(([page, table]) => {
|
|
510
|
+
// determine last page
|
|
511
|
+
const lastPage = Math.ceil(table.info.numberOfRecords /
|
|
512
|
+
(table.info.pageSize ??
|
|
513
|
+
(table.config?.pagination?.length || table.info.numberOfRecords))) - 1;
|
|
514
|
+
// determine min/max position
|
|
515
|
+
return +page < 0 ? 0 : +page > lastPage ? lastPage : +page;
|
|
516
|
+
}), distinctUntilChanged(), tap((index) => this.pageChange.emit({ index })), shareReplay(1));
|
|
517
|
+
this.colspan$ = this.tableConfig$.pipe(switchMap((config) => config.columns
|
|
518
|
+
? of(Object.values(config.columns || config.rows || {}).filter((value) => value.hidden !== true).length)
|
|
519
|
+
: this.data$.pipe(map((data) => data.length + 1))), shareReplay(1));
|
|
520
|
+
this.footerColspan$ = this.tableConfig$.pipe(map((config) => {
|
|
521
|
+
let colspan = 0;
|
|
522
|
+
Object.values(config?.footer?.columns || {}).forEach((calculations) => {
|
|
523
|
+
if (Object.values(calculations).filter((value) => value !== false)
|
|
524
|
+
.length >= 0) {
|
|
525
|
+
colspan += 1;
|
|
526
|
+
}
|
|
527
|
+
}, {});
|
|
528
|
+
return colspan;
|
|
529
|
+
}), shareReplay(1));
|
|
530
|
+
this.columnOrder = (a, b) => {
|
|
531
|
+
return (a.value.order || 0) - (b.value.order || 0);
|
|
532
|
+
};
|
|
533
|
+
this._unsubscribeFromKeyboardEvents$ = new Subject();
|
|
534
|
+
this._keyboardArrowEvent$ = fromEvent(document, 'keydown').pipe(filter((event) => [...this._navigationKeys, ...this._selectKeys].indexOf(event.key) > -1));
|
|
535
|
+
}
|
|
536
|
+
get navigationKeys() {
|
|
537
|
+
return this._navigationKeys;
|
|
538
|
+
}
|
|
539
|
+
/** navigationKeys
|
|
540
|
+
* @description An array of keyboard keys that will trigger navigation and active row, currently only supports arrow keys, home and end (omit key name from array to disable it)
|
|
541
|
+
* @type {string[]}
|
|
542
|
+
* @default ['ArrowDown', 'ArrowUp', 'ArrowLeft', 'ArrowRight', 'Home', 'End']
|
|
543
|
+
*/
|
|
544
|
+
set navigationKeys(value) {
|
|
545
|
+
this._navigationKeys = value;
|
|
546
|
+
}
|
|
547
|
+
get selectKeys() {
|
|
548
|
+
return this._selectKeys;
|
|
549
|
+
}
|
|
550
|
+
/** selectKeys
|
|
551
|
+
* @description An array of keyboard keys that will trigger row selection (omit key name from array to disable it)
|
|
552
|
+
* @type {string[]}
|
|
553
|
+
* @default ['Enter', ' ']
|
|
554
|
+
*/
|
|
555
|
+
set selectKeys(value) {
|
|
556
|
+
this._selectKeys = value;
|
|
557
|
+
}
|
|
558
|
+
get sortOrder$() {
|
|
559
|
+
return this._sortOrder$.asObservable();
|
|
560
|
+
}
|
|
561
|
+
set loading(isLoading) {
|
|
562
|
+
this._loading$.next(isLoading);
|
|
563
|
+
}
|
|
564
|
+
set paginationIndex(pageIndex) {
|
|
565
|
+
this._currentPaginationIndex$.next(pageIndex);
|
|
566
|
+
}
|
|
567
|
+
get paginationIndex() {
|
|
568
|
+
return this._currentPaginationIndex$.getValue();
|
|
569
|
+
}
|
|
570
|
+
set pagingInfo(value) {
|
|
571
|
+
if (value) {
|
|
572
|
+
this._pagingInfo$.next(value);
|
|
573
|
+
if (value.pageCurrent !== this._currentPaginationIndex$.getValue() + 1 &&
|
|
574
|
+
value.pageCurrent !== null) {
|
|
575
|
+
this.paginationIndex = value.pageCurrent - 1;
|
|
576
|
+
}
|
|
577
|
+
}
|
|
578
|
+
}
|
|
579
|
+
/** customClasses
|
|
580
|
+
* @description An object that contains custom classes for various elements in the table.
|
|
581
|
+
* @type {object} - { selectedRow: string, activeRow: string } - default classes are 'gt-selected' and 'gt-active'
|
|
582
|
+
*/
|
|
583
|
+
set customClasses(classes) {
|
|
584
|
+
this._customClasses = { ...this._customClasses, ...classes };
|
|
585
|
+
}
|
|
586
|
+
get customClasses() {
|
|
587
|
+
return this._customClasses;
|
|
588
|
+
}
|
|
589
|
+
/** isRowSelectedFn
|
|
590
|
+
* @description Function to determine if row is selected or not.
|
|
591
|
+
* @type {fn} A function that receives a row object and optional state for current selection that can be used to determine if row should be marked as selected or not. */
|
|
592
|
+
set isRowSelectedFn(fn) {
|
|
593
|
+
this._isRowSelectedFn = fn;
|
|
594
|
+
}
|
|
595
|
+
get isRowSelectedFn() {
|
|
596
|
+
return this._isRowSelectedFn;
|
|
597
|
+
}
|
|
598
|
+
/** trackRowByFn
|
|
599
|
+
* @description A function that returns a unique identifier for each row in the table to optimize rendering when data is added or removed.
|
|
600
|
+
* @type fn - TrackByFunction to retrieve unique id based on index and/or row. Defaults to using `row[this.rowIdKey]`.
|
|
601
|
+
*/
|
|
602
|
+
set trackRowByFn(fn) {
|
|
603
|
+
this._trackRowByFn = fn;
|
|
604
|
+
}
|
|
605
|
+
get trackRowByFn() {
|
|
606
|
+
return this._trackRowByFn;
|
|
607
|
+
}
|
|
608
|
+
_trackRowByFn(index, row) {
|
|
609
|
+
return this.rowIdKey ? row[this.rowIdKey] : row?._id;
|
|
610
|
+
}
|
|
611
|
+
set search(string) {
|
|
612
|
+
this._searchBy$.next(string);
|
|
613
|
+
}
|
|
614
|
+
set config(config) {
|
|
615
|
+
this._tableConfig$.next(config);
|
|
616
|
+
}
|
|
617
|
+
set data(data) {
|
|
618
|
+
if (data) {
|
|
619
|
+
this._data$.next(data);
|
|
620
|
+
}
|
|
621
|
+
}
|
|
622
|
+
set sortOrder(sortConfig) {
|
|
623
|
+
if (JSON.stringify(sortConfig) !== JSON.stringify(this._sortOrder$.value)) {
|
|
624
|
+
this.sortOrderChange.emit(sortConfig);
|
|
625
|
+
this._sortOrder$.next(sortConfig);
|
|
626
|
+
}
|
|
627
|
+
}
|
|
628
|
+
_rowClick(row, index, event) {
|
|
629
|
+
this.rowClick.emit({ row, index, event });
|
|
630
|
+
}
|
|
631
|
+
_rowActive(row, index, event) {
|
|
632
|
+
this.rowSelect.emit({ row, index, event });
|
|
633
|
+
}
|
|
634
|
+
activateRow(arg, event) {
|
|
635
|
+
if (typeof arg === 'number') {
|
|
636
|
+
this.table$
|
|
637
|
+
.pipe(pluck('data'), map((data) => data[this.paginationIndex][arg]), take(1), takeUntil(this.unsubscribe$))
|
|
638
|
+
.subscribe((row) => this._activateRow(row, arg, event));
|
|
639
|
+
}
|
|
640
|
+
else if (typeof arg === 'string') {
|
|
641
|
+
// TODO: implement hover by id
|
|
642
|
+
}
|
|
643
|
+
else {
|
|
644
|
+
this._activateRow(null, null);
|
|
645
|
+
}
|
|
646
|
+
}
|
|
647
|
+
_activateRow(row, index, event) {
|
|
648
|
+
this._rowActive$.next({ row, index, event });
|
|
649
|
+
}
|
|
650
|
+
get loading$() {
|
|
651
|
+
return this._loading$.pipe(startWith(false), map((value) => (isObservable(value) ? value : of(value))), switchMap((obs) => obs), shareReplay(1));
|
|
652
|
+
}
|
|
653
|
+
/** tableInfo$ - returns observable for table info
|
|
654
|
+
* @return Observable<TableInfo> */
|
|
655
|
+
get tableInfo$() {
|
|
656
|
+
return this._tableInfo$.asObservable().pipe(filter((info) => !!info), shareReplay(1));
|
|
657
|
+
}
|
|
658
|
+
/** tableInfo - returns the current table info
|
|
659
|
+
* @return TableInfo */
|
|
660
|
+
get tableInfo() {
|
|
661
|
+
return this._tableInfo$.getValue();
|
|
662
|
+
}
|
|
663
|
+
/** sortByKey - Sort by key in table row
|
|
664
|
+
* @param key - key to sort by
|
|
665
|
+
* @param { MouseEvent } [$event] - Mouse event triggering sort, if shift key is pressed sort key will be added to already present sort keys
|
|
666
|
+
*/
|
|
667
|
+
sortByKey(key, $event) {
|
|
668
|
+
const shiftKey = $event?.shiftKey === true;
|
|
669
|
+
const currentOrder = this._sortOrder$.value;
|
|
670
|
+
let sortOrder = 'asc';
|
|
671
|
+
let newOrder = [];
|
|
672
|
+
// if shift key is pressed while sorting...
|
|
673
|
+
if (shiftKey) {
|
|
674
|
+
// ...check if key is already sorted
|
|
675
|
+
const existingSortPosition = currentOrder.findIndex((value) => value.key === key);
|
|
676
|
+
if (existingSortPosition === -1) {
|
|
677
|
+
// ...if key is not sorted, add it to the end of the sort order
|
|
678
|
+
newOrder = [...currentOrder, { key, order: 'asc' }];
|
|
679
|
+
}
|
|
680
|
+
else {
|
|
681
|
+
// ...if key is already sorted, toggle sort order
|
|
682
|
+
sortOrder = currentOrder[existingSortPosition].order;
|
|
683
|
+
const newSortOrder = sortOrder === 'asc' ? 'desc' : 'asc';
|
|
684
|
+
newOrder = [...currentOrder];
|
|
685
|
+
newOrder[existingSortPosition] = {
|
|
686
|
+
...newOrder[existingSortPosition],
|
|
687
|
+
order: newSortOrder,
|
|
688
|
+
};
|
|
689
|
+
}
|
|
690
|
+
}
|
|
691
|
+
else {
|
|
692
|
+
// ...else if shift key is not pressed...
|
|
693
|
+
if (currentOrder.length > 0) {
|
|
694
|
+
// ...check if key is already sorted
|
|
695
|
+
const existingSortPosition = currentOrder.findIndex((value) => value.key === key);
|
|
696
|
+
// ...if key is already sorted, toggle sort order
|
|
697
|
+
if (existingSortPosition === -1) {
|
|
698
|
+
newOrder = [{ key, order: 'asc' }];
|
|
699
|
+
}
|
|
700
|
+
else {
|
|
701
|
+
sortOrder = currentOrder[existingSortPosition].order;
|
|
702
|
+
const newSortOrder = sortOrder === 'asc' ? 'desc' : 'asc';
|
|
703
|
+
newOrder = [{ key, order: newSortOrder }];
|
|
704
|
+
}
|
|
705
|
+
}
|
|
706
|
+
else {
|
|
707
|
+
// ...if key is not sorted set sort order for key to ascending
|
|
708
|
+
newOrder = [{ key, order: sortOrder }];
|
|
709
|
+
}
|
|
710
|
+
}
|
|
711
|
+
// create sort event
|
|
712
|
+
const sortEvent = {
|
|
713
|
+
key,
|
|
714
|
+
order: sortOrder,
|
|
715
|
+
currentSortOrder: newOrder,
|
|
716
|
+
addSortKey: shiftKey,
|
|
717
|
+
};
|
|
718
|
+
// if event is passed to sort function...
|
|
719
|
+
if ($event) {
|
|
720
|
+
// ...emit it as well
|
|
721
|
+
sortEvent.event = $event;
|
|
722
|
+
}
|
|
723
|
+
// emit sort event
|
|
724
|
+
this.columnSort.emit(sortEvent);
|
|
725
|
+
// if table is not lazy loaded (sorting is then handled server-side)...
|
|
726
|
+
if (!this.tableInfo?.lazyLoaded) {
|
|
727
|
+
// ...update sort order
|
|
728
|
+
this.sortOrder = newOrder;
|
|
729
|
+
}
|
|
730
|
+
}
|
|
731
|
+
nestedValue(object, mapTo, missingValue = null) {
|
|
732
|
+
const levels = mapTo.split('.');
|
|
733
|
+
return levels.reduce((previousValue, currentValue, index) => previousValue[currentValue] ||
|
|
734
|
+
(index === levels.length - 1 ? missingValue : {}), object);
|
|
735
|
+
}
|
|
736
|
+
listenToKeyboardEvents() {
|
|
737
|
+
if (!this._tableConfig?.activateRowOnKeyboardNavigation) {
|
|
738
|
+
return;
|
|
739
|
+
}
|
|
740
|
+
this._unsubscribeFromKeyboardEvents$.next(true);
|
|
741
|
+
this._keyboardArrowEvent$
|
|
742
|
+
.pipe(withLatestFrom(this.data$, this.currentPaginationIndex$, this.tableInfo$), takeUntil(this._unsubscribeFromKeyboardEvents$), takeUntil(this.unsubscribe$))
|
|
743
|
+
.subscribe(([event, rows, currentPage, tableInfo]) => {
|
|
744
|
+
const selectEvent = this._selectKeys.includes(event.key);
|
|
745
|
+
if (selectEvent && this.activeRowIndex !== null) {
|
|
746
|
+
const rowIndex = this.activeRowIndex + currentPage * (tableInfo?.pageSize ?? 0);
|
|
747
|
+
this._rowActive(rows[rowIndex], rowIndex, event);
|
|
748
|
+
return;
|
|
749
|
+
}
|
|
750
|
+
const navigationEvent = this._navigationKeys.includes(event.key);
|
|
751
|
+
if (navigationEvent) {
|
|
752
|
+
this._handleNavigationEvent(event, rows, currentPage, tableInfo);
|
|
753
|
+
}
|
|
754
|
+
});
|
|
755
|
+
}
|
|
756
|
+
unsubscribeFromKeyboardEvents(tableRef) {
|
|
757
|
+
if (!this._tableConfig?.activateRowOnKeyboardNavigation) {
|
|
758
|
+
return;
|
|
759
|
+
}
|
|
760
|
+
// only unsubscribe if table is not focused
|
|
761
|
+
if (tableRef !== document.activeElement) {
|
|
762
|
+
if (this._tableConfig?.activateRowOnHover) {
|
|
763
|
+
// unset active row
|
|
764
|
+
this.activateRow(null);
|
|
765
|
+
}
|
|
766
|
+
this._unsubscribeFromKeyboardEvents$.next(true);
|
|
767
|
+
}
|
|
768
|
+
}
|
|
769
|
+
_handleNavigationEvent(event, rows, currentPage, tableInfo) {
|
|
770
|
+
const hasPagination = (tableInfo?.pageTotal || 0) > 1 && tableInfo;
|
|
771
|
+
const lastRowIndex = rows.length - 1;
|
|
772
|
+
let newIndex = this.activeRowIndex;
|
|
773
|
+
let indexModifier = 0;
|
|
774
|
+
if (event.key === 'Home') {
|
|
775
|
+
this.paginationIndex = 0;
|
|
776
|
+
this.activateRow(0, event);
|
|
777
|
+
return;
|
|
778
|
+
}
|
|
779
|
+
if (event.key === 'End') {
|
|
780
|
+
const indexOfLastRecord = hasPagination
|
|
781
|
+
? rows.length - (tableInfo.pageTotal - 1) * tableInfo.pageSize - 1
|
|
782
|
+
: lastRowIndex;
|
|
783
|
+
if (tableInfo?.pageTotal) {
|
|
784
|
+
this.paginationIndex = tableInfo.pageTotal - 1;
|
|
785
|
+
}
|
|
786
|
+
this.activateRow(indexOfLastRecord, event);
|
|
787
|
+
return;
|
|
788
|
+
}
|
|
789
|
+
if (event.key === 'ArrowDown') {
|
|
790
|
+
indexModifier = 1;
|
|
791
|
+
}
|
|
792
|
+
else if (event.key === 'ArrowUp') {
|
|
793
|
+
indexModifier = -1;
|
|
794
|
+
}
|
|
795
|
+
if (newIndex === null) {
|
|
796
|
+
newIndex = 0;
|
|
797
|
+
}
|
|
798
|
+
else if (newIndex + indexModifier >= 0 &&
|
|
799
|
+
newIndex + indexModifier <= lastRowIndex) {
|
|
800
|
+
newIndex = newIndex + indexModifier;
|
|
801
|
+
}
|
|
802
|
+
if (hasPagination && tableInfo?.pageSize) {
|
|
803
|
+
const isNotLastPage = currentPage + 1 < tableInfo.pageTotal;
|
|
804
|
+
const recordsOnLastPage = rows.length - (tableInfo.pageTotal - 1) * tableInfo.pageSize - 1;
|
|
805
|
+
const maxIndex = isNotLastPage
|
|
806
|
+
? tableInfo?.pageSize - 1
|
|
807
|
+
: recordsOnLastPage;
|
|
808
|
+
if (event.key === 'ArrowLeft' && currentPage > 0) {
|
|
809
|
+
this.paginationIndex = currentPage - 1;
|
|
810
|
+
this.activateRow(newIndex, event);
|
|
811
|
+
return;
|
|
812
|
+
}
|
|
813
|
+
else if (event.key === 'ArrowRight' && isNotLastPage) {
|
|
814
|
+
if (currentPage + 1 === tableInfo.pageTotal - 1 &&
|
|
815
|
+
newIndex > recordsOnLastPage) {
|
|
816
|
+
this.activateRow(recordsOnLastPage, event);
|
|
817
|
+
}
|
|
818
|
+
this.paginationIndex = currentPage + 1;
|
|
819
|
+
this.activateRow(newIndex, event);
|
|
820
|
+
return;
|
|
821
|
+
}
|
|
822
|
+
if (currentPage > 0 &&
|
|
823
|
+
indexModifier < 0 &&
|
|
824
|
+
newIndex + indexModifier <= lastRowIndex &&
|
|
825
|
+
(this.activeRowIndex || 0) + indexModifier < 0) {
|
|
826
|
+
// set last row of previous page as active
|
|
827
|
+
this.activateRow(tableInfo?.pageSize - 1, event);
|
|
828
|
+
this.paginationIndex = currentPage - 1;
|
|
829
|
+
return;
|
|
830
|
+
}
|
|
831
|
+
const pageIndex = newIndex % tableInfo?.pageSize;
|
|
832
|
+
if (newIndex > maxIndex && currentPage + 1 < tableInfo.pageTotal) {
|
|
833
|
+
this.paginationIndex = currentPage + 1;
|
|
834
|
+
}
|
|
835
|
+
this.activateRow(pageIndex > maxIndex ? maxIndex : pageIndex, event);
|
|
836
|
+
return;
|
|
837
|
+
}
|
|
838
|
+
this.activateRow(newIndex, event);
|
|
839
|
+
}
|
|
840
|
+
ngOnDestroy() {
|
|
841
|
+
this.unsubscribe$.next(true);
|
|
842
|
+
this.unsubscribe$.complete();
|
|
843
|
+
}
|
|
844
|
+
}
|
|
845
|
+
CoreComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.1.1", ngImport: i0, type: CoreComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
846
|
+
CoreComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "15.1.1", type: CoreComponent, isStandalone: true, selector: "angular-generic-table", inputs: { navigationKeys: "navigationKeys", selectKeys: "selectKeys", loading: "loading", paginationIndex: "paginationIndex", pagingInfo: "pagingInfo", customClasses: "customClasses", isRowSelectedFn: "isRowSelectedFn", selection: "selection", rowIdKey: "rowIdKey", generateRowId: "generateRowId", trackRowByFn: "trackRowByFn", search: "search", config: "config", data: "data", sortOrder: "sortOrder" }, outputs: { rowClick: "rowClick", rowSelect: "rowSelect", sortOrderChange: "sortOrderChange", rowActive: "rowActive", columnSort: "columnSort", pageChange: "pageChange" }, ngImport: i0, template: "<table\n [ngClass]=\"(tableConfig$ | async)?.class || 'table'\"\n [class.table-mobile]=\"(tableConfig$ | async)?.mobileLayout\"\n [class.table-horizontal]=\"(tableConfig$ | async)?.rows\"\n [class.table-loading]=\"loading$ | async\"\n [class.gt-sticky-row-header]=\"\n (tableConfig$ | async)?.stickyHeaders?.row && (tableConfig$ | async)?.rows\n \"\n [class.gt-sticky-column-header]=\"\n (tableConfig$ | async)?.stickyHeaders?.column\n \"\n [attr.aria-busy]=\"(loading$ | async) === true ? true : null\"\n [tabindex]=\"(tableConfig$ | async)?.activateRowOnKeyboardNavigation ? 0 : -1\"\n #tableRef\n (focus)=\"listenToKeyboardEvents()\"\n (focusout)=\"unsubscribeFromKeyboardEvents(tableRef)\"\n (mouseenter)=\"listenToKeyboardEvents()\"\n (mouseleave)=\"unsubscribeFromKeyboardEvents(tableRef)\"\n>\n <thead>\n <tr\n *ngIf=\"{\n config: (tableConfig$ | async)!,\n isLoading: loading$ | async\n } as table\"\n >\n <ng-container\n *ngFor=\"let column of table.config?.columns | keyvalue: columnOrder\"\n >\n <th\n *ngIf=\"!column.value?.hidden\"\n ngClass=\"{{ (column.key | dashCase) + '-column' }} {{\n column.value.class\n }}\"\n [class.disabled]=\"table.isLoading\"\n [attr.aria-sort]=\"sortOrder$ | async | sortClass: column.key:'aria'\"\n [class.gt-sortable]=\"true\"\n scope=\"col\"\n >\n <button\n *ngIf=\"column.value?.sortable\"\n [attr.data-sort-order]=\"\n sortOrder$ | async | sortClass: column.key:'order'\n \"\n class=\"gt-sort\"\n (click)=\"\n table.isLoading ||\n !column.value.sortable ||\n sortByKey(column.key, $event)\n \"\n >\n <span *ngIf=\"column.value?.header !== false\">{{\n column.value.header || column.key | capitalCase\n }}</span>\n </button>\n <span\n *ngIf=\"!column.value?.sortable && column.value?.header !== false\"\n >{{ column.value.header || column.key | capitalCase }}</span\n >\n </th>\n </ng-container>\n <ng-container\n *ngIf=\"\n ((table?.config?.rows | keyvalue: columnOrder) || [])[0] as headerRow\n \"\n >\n <th\n class=\"row-header\"\n [attr.aria-sort]=\"\n sortOrder$ | async | sortClass: headerRow.key:'aria'\n \"\n ngClass=\"{{ headerRow.value.sortable ? 'sort ' : '' }} {{\n sortOrder$ | async | sortClass: headerRow.key\n }} {{ (headerRow.key | dashCase) + '-column' }}\"\n (click)=\"\n table.isLoading ||\n !headerRow.value.sortable ||\n sortByKey(headerRow.key, $event)\n \"\n scope=\"col\"\n >\n <ng-container *ngIf=\"headerRow?.value?.header !== false\">{{\n headerRow?.value?.header || headerRow.key | capitalCase\n }}</ng-container>\n </th>\n <th\n *ngFor=\"let column of ((table$ | async)?.data || [])[0]\"\n ngClass=\"{{ headerRow.value.class }}\"\n >\n <ng-container\n [ngTemplateOutlet]=\"\n (table.config.rows || {})[headerRow.key].templateRef\n ? templateRef\n : (table.config.rows || {})[headerRow.key].transform\n ? transformData\n : rawData\n \"\n [ngTemplateOutletContext]=\"{\n row: column,\n column: headerRow,\n transform: (table.config.rows || {})[headerRow.key].transform,\n templateRef: (table.config.rows || {})[headerRow.key].templateRef,\n index: 0\n }\"\n >\n </ng-container>\n </th>\n </ng-container>\n </tr>\n </thead>\n <tbody *ngIf=\"loading$ | async; else tableContent\">\n <tr>\n <td class=\"p-0\" [colSpan]=\"colspan$ | async\">\n <ng-content select=\".table-loading\"></ng-content>\n </td>\n </tr>\n </tbody>\n <tfoot *ngIf=\"(table$ | async)! as table\">\n <ng-container *ngIf=\"table.data.length > 0 && !(loading$ | async)\">\n <ng-container *ngIf=\"(calculations$ | async)! as calculations\">\n <tr\n *ngFor=\"let calculation of calculations.calculations; let i = index\"\n >\n <ng-container\n *ngIf=\"{\n showHeader: (colspan$ | async) !== (footerColspan$ | async)\n } as footerRow\"\n >\n <th\n *ngIf=\"footerRow.showHeader\"\n [colSpan]=\"\n ((colspan$ | async) || 0) - ((footerColspan$ | async) || 0)\n \"\n scope=\"row\"\n >\n <ng-container\n *ngIf=\"table.config?.footer?.headers?.[calculation] as showHeader\"\n >{{showHeader === true ? (calculation | capitalCase): table.config.footer?.headers?.[calculation]}}\n </ng-container>\n </th>\n <ng-container\n *ngFor=\"\n let column of table.config?.columns | keyvalue: columnOrder\n \"\n >\n <td\n *ngIf=\"\n !column.value?.hidden && calculations.calculated[column.key]\n \"\n ngClass=\"{{ (column.key | dashCase) + '-column' }} {{\n column.value.class\n }}\"\n [attr.data-header]=\"\n !footerRow.showHeader && table.config.footer?.headers?.[calculation]\n ? table.config.footer?.headers?.[calculation] === true ? (calculation | capitalCase) : table.config.footer?.headers?.[calculation]\n : null\n \"\n [attr.data-label]=\"\n table.config.mobileLayout && column.value.mobileHeader\n ? column.value.mobileHeader !== true\n ? column.value.mobileHeader\n : (column.value.header || column.key | capitalCase)\n : null\n \"\n [class.gt-no-content]=\"\n !calculations.calculated[column.key][calculation]\n \"\n >\n <ng-container\n [ngTemplateOutlet]=\"\n (table.config.columns || {})[column.key].templateRef\n ? templateRef\n : (table.config.columns || {})[column.key].transform\n ? transformFooter\n : rawFooter\n \"\n [ngTemplateOutletContext]=\"{\n value: calculations.calculated[column.key][calculation],\n row: calculations.calculated[column.key],\n column: calculation,\n templateRef: (table.config.columns || {})[column.key]\n .templateRef,\n transform: (table.config.columns || {})[column.key]\n .transform\n }\"\n ></ng-container>\n </td>\n </ng-container>\n </ng-container>\n </tr>\n </ng-container>\n </ng-container>\n </tfoot>\n</table>\n<ng-template #tableContent>\n <ng-container *ngIf=\"(table$ | async)! as table\">\n <tbody *ngIf=\"(table!.data![0] || table!.data!).length > 0; else noData\">\n <ng-container *ngIf=\"table.config.columns\">\n <tr\n *ngFor=\"\n let row of table!.data![\n table.info.lazyLoaded ? 0 : (currentPaginationIndex$ | async) || 0\n ];\n trackBy: trackRowByFn;\n let i = index\n \"\n [attr.id]=\"'tableRow_' + i\"\n (click)=\"table?.config?.rowClick && _rowClick(row, i, $event)\"\n (mouseenter)=\"\n table?.config?.activateRowOnHover && _activateRow(row, i, $event)\n \"\n (mouseleave)=\"\n table?.config?.activateRowOnHover &&\n _activateRow(null, null, $event)\n \"\n [ngClass]=\"[\n !!isRowSelectedFn\n ? (row\n | rowSelection\n : selection\n : isRowSelectedFn\n : customClasses.selectedRow)\n : '',\n (rowActive$ | async)?.index === i ? customClasses.activeRow : ''\n ]\"\n >\n <ng-container\n *ngFor=\"let column of table.config?.columns | keyvalue: columnOrder\"\n >\n <td\n *ngIf=\"!column.value?.hidden\"\n ngClass=\"{{ (column.key | dashCase) + '-column' }} {{\n column.value.class\n }}\"\n [attr.data-label]=\"\n table.config.mobileLayout && column.value.mobileHeader\n ? column.value.mobileHeader !== true\n ? column.value.mobileHeader\n : (column.value.header || column.key | capitalCase)\n : null\n \"\n >\n <ng-container\n [ngTemplateOutlet]=\"\n (searchBy$ | async) &&\n !(table.config.columns || {})[column.key].templateRef\n ? highlighted\n : (table.config.columns || {})[column.key].templateRef\n ? templateRef\n : (table.config.columns || {})[column.key].transform\n ? transformData\n : rawData\n \"\n [ngTemplateOutletContext]=\"{\n row: row,\n column: column,\n search: (searchBy$ | async),\n transform: (table.config.columns || {})[column.key].transform,\n templateRef: (table.config.columns || {})[column.key]\n .templateRef,\n index: i,\n data: table.data[\n table.info.lazyLoaded\n ? 0\n : (currentPaginationIndex$ | async) || 0\n ]\n }\"\n ></ng-container>\n </td>\n </ng-container>\n </tr>\n </ng-container>\n <ng-container *ngIf=\"table.config.rows\">\n <ng-container\n *ngFor=\"\n let row of table?.config?.rows | keyvalue: columnOrder | slice: 1;\n let i = index\n \"\n >\n <tr\n *ngIf=\"!row.value?.hidden\"\n [attr.id]=\"'tableRow_' + i\"\n ngClass=\"{{ (row.key | dashCase) + '-row' }}\"\n (click)=\"table?.config?.rowClick && _rowClick(row, i, $event)\"\n (mouseenter)=\"\n table?.config?.activateRowOnHover && _activateRow(row, i, $event)\n \"\n (mouseleave)=\"\n table?.config?.activateRowOnHover &&\n _activateRow(null, null, $event)\n \"\n [ngClass]=\"[\n !!isRowSelectedFn\n ? (row\n | rowSelection\n : selection\n : isRowSelectedFn\n : customClasses.selectedRow)\n : '',\n (rowActive$ | async)?.index === i ? customClasses.activeRow : ''\n ]\"\n >\n <th class=\"row-header\" scope=\"row\">\n {{ row.value.header || row.key | capitalCase }}\n </th>\n <td\n *ngFor=\"let column of (table?.data || [])[0]; let y = index\"\n ngClass=\"{{ row.value.class }}\"\n >\n <ng-container\n [ngTemplateOutlet]=\"\n (table.config.rows || {})[row.key].templateRef\n ? templateRef\n : (table.config.rows || {})[row.key].transform\n ? transformData\n : rawData\n \"\n [ngTemplateOutletContext]=\"{\n row: column,\n column: row,\n transform: (table.config.rows || {})[row.key].transform,\n templateRef: (table.config.rows || {})[row.key].templateRef,\n index: table.config.rows ? y : i,\n data: table.data[\n table.info.lazyLoaded\n ? 0\n : (currentPaginationIndex$ | async) || 0\n ]\n }\"\n >\n </ng-container>\n </td>\n </tr>\n </ng-container>\n </ng-container>\n </tbody>\n </ng-container>\n</ng-template>\n<ng-template #noData>\n <tbody>\n <tr>\n <td class=\"p-0\" [colSpan]=\"colspan$ | async\">\n <ng-content select=\".table-no-data\"></ng-content>\n </td>\n </tr>\n </tbody>\n</ng-template>\n<ng-template\n #highlighted\n let-row=\"row\"\n let-column=\"column\"\n let-search=\"search\"\n let-transform=\"transform\"\n>\n <div\n *ngIf=\"!transform\"\n [innerHTML]=\"row[column.key] | highlight: search\"\n ></div>\n <div\n *ngIf=\"transform\"\n [innerHTML]=\"\n row[column.key]\n | dynamicPipe: transform.pipe:transform?.args\n | highlight: search\n \"\n ></div>\n</ng-template>\n<ng-template #rawData let-row=\"row\" let-column=\"column\">\n {{ row[column.key] }}\n</ng-template>\n<ng-template\n #transformData\n let-row=\"row\"\n let-column=\"column\"\n let-transform=\"transform\"\n let-data=\"data\"\n>\n {{ row[column.key] | dynamicPipe: transform.pipe:transform?.args }}\n</ng-template>\n<ng-template #transformFooter let-value=\"value\" let-transform=\"transform\">\n {{\n (value | dynamicPipe: transform.pipe:transform?.args) ||\n (tableConfig$ | async)?.footer?.emptyContent\n }}\n</ng-template>\n<ng-template #rawFooter let-value=\"value\">\n {{ value || (tableConfig$ | async)?.footer?.emptyContent }}\n</ng-template>\n<ng-template\n #templateRef\n let-row=\"row\"\n let-column=\"column\"\n let-index=\"index\"\n let-templateRef=\"templateRef\"\n let-data=\"data\"\n>\n <ng-container\n [ngTemplateOutlet]=\"templateRef\"\n [ngTemplateOutletContext]=\"{\n row: row,\n col: column,\n index: index,\n data: data\n }\"\n ></ng-container>\n</ng-template>\n", dependencies: [{ kind: "pipe", type: CapitalCasePipe, name: "capitalCase" }, { kind: "pipe", type: KeyValuePipe, name: "keyvalue" }, { kind: "pipe", type: SortClassPipe, name: "sortClass" }, { kind: "pipe", type: DashCasePipe, name: "dashCase" }, { kind: "pipe", type: RowSelectionPipe, name: "rowSelection" }, { kind: "pipe", type: AsyncPipe, name: "async" }, { kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "pipe", type: SlicePipe, name: "slice" }, { kind: "pipe", type: DynamicPipe, name: "dynamicPipe" }, { kind: "pipe", type: HighlightPipe, name: "highlight" }, { kind: "directive", type: NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
847
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.1.1", ngImport: i0, type: CoreComponent, decorators: [{
|
|
848
|
+
type: Component,
|
|
849
|
+
args: [{ selector: 'angular-generic-table', changeDetection: ChangeDetectionStrategy.OnPush, standalone: true, imports: [
|
|
850
|
+
CapitalCasePipe,
|
|
851
|
+
KeyValuePipe,
|
|
852
|
+
SortClassPipe,
|
|
853
|
+
DashCasePipe,
|
|
854
|
+
RowSelectionPipe,
|
|
855
|
+
AsyncPipe,
|
|
856
|
+
NgTemplateOutlet,
|
|
857
|
+
SlicePipe,
|
|
858
|
+
DynamicPipe,
|
|
859
|
+
HighlightPipe,
|
|
860
|
+
NgClass,
|
|
861
|
+
NgIf,
|
|
862
|
+
NgForOf,
|
|
863
|
+
], template: "<table\n [ngClass]=\"(tableConfig$ | async)?.class || 'table'\"\n [class.table-mobile]=\"(tableConfig$ | async)?.mobileLayout\"\n [class.table-horizontal]=\"(tableConfig$ | async)?.rows\"\n [class.table-loading]=\"loading$ | async\"\n [class.gt-sticky-row-header]=\"\n (tableConfig$ | async)?.stickyHeaders?.row && (tableConfig$ | async)?.rows\n \"\n [class.gt-sticky-column-header]=\"\n (tableConfig$ | async)?.stickyHeaders?.column\n \"\n [attr.aria-busy]=\"(loading$ | async) === true ? true : null\"\n [tabindex]=\"(tableConfig$ | async)?.activateRowOnKeyboardNavigation ? 0 : -1\"\n #tableRef\n (focus)=\"listenToKeyboardEvents()\"\n (focusout)=\"unsubscribeFromKeyboardEvents(tableRef)\"\n (mouseenter)=\"listenToKeyboardEvents()\"\n (mouseleave)=\"unsubscribeFromKeyboardEvents(tableRef)\"\n>\n <thead>\n <tr\n *ngIf=\"{\n config: (tableConfig$ | async)!,\n isLoading: loading$ | async\n } as table\"\n >\n <ng-container\n *ngFor=\"let column of table.config?.columns | keyvalue: columnOrder\"\n >\n <th\n *ngIf=\"!column.value?.hidden\"\n ngClass=\"{{ (column.key | dashCase) + '-column' }} {{\n column.value.class\n }}\"\n [class.disabled]=\"table.isLoading\"\n [attr.aria-sort]=\"sortOrder$ | async | sortClass: column.key:'aria'\"\n [class.gt-sortable]=\"true\"\n scope=\"col\"\n >\n <button\n *ngIf=\"column.value?.sortable\"\n [attr.data-sort-order]=\"\n sortOrder$ | async | sortClass: column.key:'order'\n \"\n class=\"gt-sort\"\n (click)=\"\n table.isLoading ||\n !column.value.sortable ||\n sortByKey(column.key, $event)\n \"\n >\n <span *ngIf=\"column.value?.header !== false\">{{\n column.value.header || column.key | capitalCase\n }}</span>\n </button>\n <span\n *ngIf=\"!column.value?.sortable && column.value?.header !== false\"\n >{{ column.value.header || column.key | capitalCase }}</span\n >\n </th>\n </ng-container>\n <ng-container\n *ngIf=\"\n ((table?.config?.rows | keyvalue: columnOrder) || [])[0] as headerRow\n \"\n >\n <th\n class=\"row-header\"\n [attr.aria-sort]=\"\n sortOrder$ | async | sortClass: headerRow.key:'aria'\n \"\n ngClass=\"{{ headerRow.value.sortable ? 'sort ' : '' }} {{\n sortOrder$ | async | sortClass: headerRow.key\n }} {{ (headerRow.key | dashCase) + '-column' }}\"\n (click)=\"\n table.isLoading ||\n !headerRow.value.sortable ||\n sortByKey(headerRow.key, $event)\n \"\n scope=\"col\"\n >\n <ng-container *ngIf=\"headerRow?.value?.header !== false\">{{\n headerRow?.value?.header || headerRow.key | capitalCase\n }}</ng-container>\n </th>\n <th\n *ngFor=\"let column of ((table$ | async)?.data || [])[0]\"\n ngClass=\"{{ headerRow.value.class }}\"\n >\n <ng-container\n [ngTemplateOutlet]=\"\n (table.config.rows || {})[headerRow.key].templateRef\n ? templateRef\n : (table.config.rows || {})[headerRow.key].transform\n ? transformData\n : rawData\n \"\n [ngTemplateOutletContext]=\"{\n row: column,\n column: headerRow,\n transform: (table.config.rows || {})[headerRow.key].transform,\n templateRef: (table.config.rows || {})[headerRow.key].templateRef,\n index: 0\n }\"\n >\n </ng-container>\n </th>\n </ng-container>\n </tr>\n </thead>\n <tbody *ngIf=\"loading$ | async; else tableContent\">\n <tr>\n <td class=\"p-0\" [colSpan]=\"colspan$ | async\">\n <ng-content select=\".table-loading\"></ng-content>\n </td>\n </tr>\n </tbody>\n <tfoot *ngIf=\"(table$ | async)! as table\">\n <ng-container *ngIf=\"table.data.length > 0 && !(loading$ | async)\">\n <ng-container *ngIf=\"(calculations$ | async)! as calculations\">\n <tr\n *ngFor=\"let calculation of calculations.calculations; let i = index\"\n >\n <ng-container\n *ngIf=\"{\n showHeader: (colspan$ | async) !== (footerColspan$ | async)\n } as footerRow\"\n >\n <th\n *ngIf=\"footerRow.showHeader\"\n [colSpan]=\"\n ((colspan$ | async) || 0) - ((footerColspan$ | async) || 0)\n \"\n scope=\"row\"\n >\n <ng-container\n *ngIf=\"table.config?.footer?.headers?.[calculation] as showHeader\"\n >{{showHeader === true ? (calculation | capitalCase): table.config.footer?.headers?.[calculation]}}\n </ng-container>\n </th>\n <ng-container\n *ngFor=\"\n let column of table.config?.columns | keyvalue: columnOrder\n \"\n >\n <td\n *ngIf=\"\n !column.value?.hidden && calculations.calculated[column.key]\n \"\n ngClass=\"{{ (column.key | dashCase) + '-column' }} {{\n column.value.class\n }}\"\n [attr.data-header]=\"\n !footerRow.showHeader && table.config.footer?.headers?.[calculation]\n ? table.config.footer?.headers?.[calculation] === true ? (calculation | capitalCase) : table.config.footer?.headers?.[calculation]\n : null\n \"\n [attr.data-label]=\"\n table.config.mobileLayout && column.value.mobileHeader\n ? column.value.mobileHeader !== true\n ? column.value.mobileHeader\n : (column.value.header || column.key | capitalCase)\n : null\n \"\n [class.gt-no-content]=\"\n !calculations.calculated[column.key][calculation]\n \"\n >\n <ng-container\n [ngTemplateOutlet]=\"\n (table.config.columns || {})[column.key].templateRef\n ? templateRef\n : (table.config.columns || {})[column.key].transform\n ? transformFooter\n : rawFooter\n \"\n [ngTemplateOutletContext]=\"{\n value: calculations.calculated[column.key][calculation],\n row: calculations.calculated[column.key],\n column: calculation,\n templateRef: (table.config.columns || {})[column.key]\n .templateRef,\n transform: (table.config.columns || {})[column.key]\n .transform\n }\"\n ></ng-container>\n </td>\n </ng-container>\n </ng-container>\n </tr>\n </ng-container>\n </ng-container>\n </tfoot>\n</table>\n<ng-template #tableContent>\n <ng-container *ngIf=\"(table$ | async)! as table\">\n <tbody *ngIf=\"(table!.data![0] || table!.data!).length > 0; else noData\">\n <ng-container *ngIf=\"table.config.columns\">\n <tr\n *ngFor=\"\n let row of table!.data![\n table.info.lazyLoaded ? 0 : (currentPaginationIndex$ | async) || 0\n ];\n trackBy: trackRowByFn;\n let i = index\n \"\n [attr.id]=\"'tableRow_' + i\"\n (click)=\"table?.config?.rowClick && _rowClick(row, i, $event)\"\n (mouseenter)=\"\n table?.config?.activateRowOnHover && _activateRow(row, i, $event)\n \"\n (mouseleave)=\"\n table?.config?.activateRowOnHover &&\n _activateRow(null, null, $event)\n \"\n [ngClass]=\"[\n !!isRowSelectedFn\n ? (row\n | rowSelection\n : selection\n : isRowSelectedFn\n : customClasses.selectedRow)\n : '',\n (rowActive$ | async)?.index === i ? customClasses.activeRow : ''\n ]\"\n >\n <ng-container\n *ngFor=\"let column of table.config?.columns | keyvalue: columnOrder\"\n >\n <td\n *ngIf=\"!column.value?.hidden\"\n ngClass=\"{{ (column.key | dashCase) + '-column' }} {{\n column.value.class\n }}\"\n [attr.data-label]=\"\n table.config.mobileLayout && column.value.mobileHeader\n ? column.value.mobileHeader !== true\n ? column.value.mobileHeader\n : (column.value.header || column.key | capitalCase)\n : null\n \"\n >\n <ng-container\n [ngTemplateOutlet]=\"\n (searchBy$ | async) &&\n !(table.config.columns || {})[column.key].templateRef\n ? highlighted\n : (table.config.columns || {})[column.key].templateRef\n ? templateRef\n : (table.config.columns || {})[column.key].transform\n ? transformData\n : rawData\n \"\n [ngTemplateOutletContext]=\"{\n row: row,\n column: column,\n search: (searchBy$ | async),\n transform: (table.config.columns || {})[column.key].transform,\n templateRef: (table.config.columns || {})[column.key]\n .templateRef,\n index: i,\n data: table.data[\n table.info.lazyLoaded\n ? 0\n : (currentPaginationIndex$ | async) || 0\n ]\n }\"\n ></ng-container>\n </td>\n </ng-container>\n </tr>\n </ng-container>\n <ng-container *ngIf=\"table.config.rows\">\n <ng-container\n *ngFor=\"\n let row of table?.config?.rows | keyvalue: columnOrder | slice: 1;\n let i = index\n \"\n >\n <tr\n *ngIf=\"!row.value?.hidden\"\n [attr.id]=\"'tableRow_' + i\"\n ngClass=\"{{ (row.key | dashCase) + '-row' }}\"\n (click)=\"table?.config?.rowClick && _rowClick(row, i, $event)\"\n (mouseenter)=\"\n table?.config?.activateRowOnHover && _activateRow(row, i, $event)\n \"\n (mouseleave)=\"\n table?.config?.activateRowOnHover &&\n _activateRow(null, null, $event)\n \"\n [ngClass]=\"[\n !!isRowSelectedFn\n ? (row\n | rowSelection\n : selection\n : isRowSelectedFn\n : customClasses.selectedRow)\n : '',\n (rowActive$ | async)?.index === i ? customClasses.activeRow : ''\n ]\"\n >\n <th class=\"row-header\" scope=\"row\">\n {{ row.value.header || row.key | capitalCase }}\n </th>\n <td\n *ngFor=\"let column of (table?.data || [])[0]; let y = index\"\n ngClass=\"{{ row.value.class }}\"\n >\n <ng-container\n [ngTemplateOutlet]=\"\n (table.config.rows || {})[row.key].templateRef\n ? templateRef\n : (table.config.rows || {})[row.key].transform\n ? transformData\n : rawData\n \"\n [ngTemplateOutletContext]=\"{\n row: column,\n column: row,\n transform: (table.config.rows || {})[row.key].transform,\n templateRef: (table.config.rows || {})[row.key].templateRef,\n index: table.config.rows ? y : i,\n data: table.data[\n table.info.lazyLoaded\n ? 0\n : (currentPaginationIndex$ | async) || 0\n ]\n }\"\n >\n </ng-container>\n </td>\n </tr>\n </ng-container>\n </ng-container>\n </tbody>\n </ng-container>\n</ng-template>\n<ng-template #noData>\n <tbody>\n <tr>\n <td class=\"p-0\" [colSpan]=\"colspan$ | async\">\n <ng-content select=\".table-no-data\"></ng-content>\n </td>\n </tr>\n </tbody>\n</ng-template>\n<ng-template\n #highlighted\n let-row=\"row\"\n let-column=\"column\"\n let-search=\"search\"\n let-transform=\"transform\"\n>\n <div\n *ngIf=\"!transform\"\n [innerHTML]=\"row[column.key] | highlight: search\"\n ></div>\n <div\n *ngIf=\"transform\"\n [innerHTML]=\"\n row[column.key]\n | dynamicPipe: transform.pipe:transform?.args\n | highlight: search\n \"\n ></div>\n</ng-template>\n<ng-template #rawData let-row=\"row\" let-column=\"column\">\n {{ row[column.key] }}\n</ng-template>\n<ng-template\n #transformData\n let-row=\"row\"\n let-column=\"column\"\n let-transform=\"transform\"\n let-data=\"data\"\n>\n {{ row[column.key] | dynamicPipe: transform.pipe:transform?.args }}\n</ng-template>\n<ng-template #transformFooter let-value=\"value\" let-transform=\"transform\">\n {{\n (value | dynamicPipe: transform.pipe:transform?.args) ||\n (tableConfig$ | async)?.footer?.emptyContent\n }}\n</ng-template>\n<ng-template #rawFooter let-value=\"value\">\n {{ value || (tableConfig$ | async)?.footer?.emptyContent }}\n</ng-template>\n<ng-template\n #templateRef\n let-row=\"row\"\n let-column=\"column\"\n let-index=\"index\"\n let-templateRef=\"templateRef\"\n let-data=\"data\"\n>\n <ng-container\n [ngTemplateOutlet]=\"templateRef\"\n [ngTemplateOutletContext]=\"{\n row: row,\n col: column,\n index: index,\n data: data\n }\"\n ></ng-container>\n</ng-template>\n" }]
|
|
864
|
+
}], propDecorators: { navigationKeys: [{
|
|
865
|
+
type: Input
|
|
866
|
+
}], selectKeys: [{
|
|
867
|
+
type: Input
|
|
868
|
+
}], loading: [{
|
|
869
|
+
type: Input
|
|
870
|
+
}], paginationIndex: [{
|
|
871
|
+
type: Input
|
|
872
|
+
}], pagingInfo: [{
|
|
873
|
+
type: Input
|
|
874
|
+
}], customClasses: [{
|
|
875
|
+
type: Input
|
|
876
|
+
}], isRowSelectedFn: [{
|
|
877
|
+
type: Input
|
|
878
|
+
}], selection: [{
|
|
879
|
+
type: Input
|
|
880
|
+
}], rowIdKey: [{
|
|
881
|
+
type: Input
|
|
882
|
+
}], generateRowId: [{
|
|
883
|
+
type: Input
|
|
884
|
+
}], trackRowByFn: [{
|
|
885
|
+
type: Input
|
|
886
|
+
}], search: [{
|
|
887
|
+
type: Input
|
|
888
|
+
}], config: [{
|
|
889
|
+
type: Input
|
|
890
|
+
}], data: [{
|
|
891
|
+
type: Input
|
|
892
|
+
}], sortOrder: [{
|
|
893
|
+
type: Input
|
|
894
|
+
}], rowClick: [{
|
|
895
|
+
type: Output
|
|
896
|
+
}], rowSelect: [{
|
|
897
|
+
type: Output
|
|
898
|
+
}], sortOrderChange: [{
|
|
899
|
+
type: Output
|
|
900
|
+
}], rowActive: [{
|
|
901
|
+
type: Output
|
|
902
|
+
}], columnSort: [{
|
|
903
|
+
type: Output
|
|
904
|
+
}], pageChange: [{
|
|
905
|
+
type: Output
|
|
906
|
+
}] } });
|
|
907
|
+
|
|
908
|
+
class GtDeltaComponent {
|
|
909
|
+
get value() {
|
|
910
|
+
return this._value;
|
|
911
|
+
}
|
|
912
|
+
set value(value) {
|
|
913
|
+
this._value = value;
|
|
914
|
+
}
|
|
915
|
+
get deltaTemplate() {
|
|
916
|
+
return this._deltaTemplate;
|
|
917
|
+
}
|
|
918
|
+
constructor() {
|
|
919
|
+
this.Math = Math;
|
|
920
|
+
this.Number = Number;
|
|
921
|
+
this.data = [];
|
|
922
|
+
this.index = 0;
|
|
923
|
+
this.classes = {
|
|
924
|
+
span: 'gt-delta',
|
|
925
|
+
positive: 'text-success',
|
|
926
|
+
negative: 'text-danger',
|
|
927
|
+
};
|
|
928
|
+
this.key = 'value';
|
|
929
|
+
this.notApplicableValue = null;
|
|
930
|
+
this.initialValue = null;
|
|
931
|
+
}
|
|
932
|
+
set deltaTemplate(deltaTemplate) {
|
|
933
|
+
this._deltaTemplate = deltaTemplate;
|
|
934
|
+
}
|
|
935
|
+
ngOnChanges(changes) {
|
|
936
|
+
if (!changes.data.currentValue) {
|
|
937
|
+
return;
|
|
938
|
+
}
|
|
939
|
+
const data = changes.data?.currentValue;
|
|
940
|
+
const index = changes.index?.currentValue || this.index;
|
|
941
|
+
const baseIndex = changes.baseIndex?.currentValue;
|
|
942
|
+
const key = changes.key?.currentValue || this.key;
|
|
943
|
+
const initialValue = changes.initialValue?.currentValue || this.initialValue;
|
|
944
|
+
const deltaValue = index === 0
|
|
945
|
+
? initialValue
|
|
946
|
+
: data[index][key] -
|
|
947
|
+
(baseIndex === undefined
|
|
948
|
+
? data[index - 1][key]
|
|
949
|
+
: data[baseIndex][key]);
|
|
950
|
+
const baseValue = index === 0
|
|
951
|
+
? 1
|
|
952
|
+
: baseIndex === undefined
|
|
953
|
+
? data[index - 1][key]
|
|
954
|
+
: data[baseIndex][key];
|
|
955
|
+
const relative = index === 0
|
|
956
|
+
? initialValue
|
|
957
|
+
: Math.sign(deltaValue) * Math.abs(deltaValue / baseValue);
|
|
958
|
+
this.value = {
|
|
959
|
+
relative: Number.isFinite(relative)
|
|
960
|
+
? relative
|
|
961
|
+
: index === 0
|
|
962
|
+
? initialValue
|
|
963
|
+
: changes.notApplicableValue?.currentValue || this.notApplicableValue,
|
|
964
|
+
absolute: deltaValue,
|
|
965
|
+
};
|
|
966
|
+
}
|
|
967
|
+
}
|
|
968
|
+
GtDeltaComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.1.1", ngImport: i0, type: GtDeltaComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
969
|
+
GtDeltaComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "15.1.1", type: GtDeltaComponent, isStandalone: true, selector: "gt-delta", inputs: { deltaTemplate: "deltaTemplate", data: "data", index: "index", baseIndex: "baseIndex", classes: "classes", key: "key", notApplicableValue: "notApplicableValue", initialValue: "initialValue" }, usesOnChanges: true, ngImport: i0, template: `<span
|
|
970
|
+
*ngIf="value as delta"
|
|
971
|
+
[class]="[
|
|
972
|
+
classes.span,
|
|
973
|
+
delta.absolute > 0
|
|
974
|
+
? classes.positive
|
|
975
|
+
: delta.absolute < 0
|
|
976
|
+
? classes.negative
|
|
977
|
+
: null
|
|
978
|
+
]"
|
|
979
|
+
[class.gt-delta-positive]="delta.absolute > 0"
|
|
980
|
+
[class.gt-delta-negative]="delta.absolute < 0"
|
|
981
|
+
><ng-container
|
|
982
|
+
*ngTemplateOutlet="deltaTemplate || defaultTemplate; context: { delta }"
|
|
983
|
+
></ng-container>
|
|
984
|
+
</span>
|
|
985
|
+
<ng-template #defaultTemplate let-delta="delta">
|
|
986
|
+
<span *ngIf="delta.relative">{{
|
|
987
|
+
delta.relative | percent: '1.0-2'
|
|
988
|
+
}}</span>
|
|
989
|
+
</ng-template>`, isInline: true, styles: [":host{display:inline-block}\n"], dependencies: [{ kind: "pipe", type: PercentPipe, name: "percent" }, { kind: "directive", type: NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
990
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.1.1", ngImport: i0, type: GtDeltaComponent, decorators: [{
|
|
991
|
+
type: Component,
|
|
992
|
+
args: [{ selector: 'gt-delta', template: `<span
|
|
993
|
+
*ngIf="value as delta"
|
|
994
|
+
[class]="[
|
|
995
|
+
classes.span,
|
|
996
|
+
delta.absolute > 0
|
|
997
|
+
? classes.positive
|
|
998
|
+
: delta.absolute < 0
|
|
999
|
+
? classes.negative
|
|
1000
|
+
: null
|
|
1001
|
+
]"
|
|
1002
|
+
[class.gt-delta-positive]="delta.absolute > 0"
|
|
1003
|
+
[class.gt-delta-negative]="delta.absolute < 0"
|
|
1004
|
+
><ng-container
|
|
1005
|
+
*ngTemplateOutlet="deltaTemplate || defaultTemplate; context: { delta }"
|
|
1006
|
+
></ng-container>
|
|
1007
|
+
</span>
|
|
1008
|
+
<ng-template #defaultTemplate let-delta="delta">
|
|
1009
|
+
<span *ngIf="delta.relative">{{
|
|
1010
|
+
delta.relative | percent: '1.0-2'
|
|
1011
|
+
}}</span>
|
|
1012
|
+
</ng-template>`, changeDetection: ChangeDetectionStrategy.OnPush, standalone: true, imports: [PercentPipe, NgIf, NgTemplateOutlet], styles: [":host{display:inline-block}\n"] }]
|
|
1013
|
+
}], ctorParameters: function () { return []; }, propDecorators: { deltaTemplate: [{
|
|
1014
|
+
type: Input
|
|
1015
|
+
}], data: [{
|
|
1016
|
+
type: Input
|
|
1017
|
+
}], index: [{
|
|
1018
|
+
type: Input
|
|
1019
|
+
}], baseIndex: [{
|
|
1020
|
+
type: Input
|
|
1021
|
+
}], classes: [{
|
|
1022
|
+
type: Input
|
|
1023
|
+
}], key: [{
|
|
1024
|
+
type: Input
|
|
1025
|
+
}], notApplicableValue: [{
|
|
1026
|
+
type: Input
|
|
1027
|
+
}], initialValue: [{
|
|
1028
|
+
type: Input
|
|
1029
|
+
}] } });
|
|
1030
|
+
|
|
1031
|
+
class GenericTableCoreModule {
|
|
1032
|
+
}
|
|
1033
|
+
GenericTableCoreModule.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.1.1", ngImport: i0, type: GenericTableCoreModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule });
|
|
1034
|
+
GenericTableCoreModule.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "15.1.1", ngImport: i0, type: GenericTableCoreModule, imports: [CommonModule,
|
|
1035
|
+
CoreComponent,
|
|
1036
|
+
SortClassPipe,
|
|
1037
|
+
DashCasePipe,
|
|
1038
|
+
HighlightPipe,
|
|
1039
|
+
RowSelectionPipe,
|
|
1040
|
+
CapitalCasePipe,
|
|
1041
|
+
CapitalCasePipe,
|
|
1042
|
+
DynamicPipe,
|
|
1043
|
+
GtDeltaComponent], exports: [CoreComponent, GtDeltaComponent] });
|
|
1044
|
+
GenericTableCoreModule.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "15.1.1", ngImport: i0, type: GenericTableCoreModule, imports: [CommonModule,
|
|
1045
|
+
CoreComponent,
|
|
1046
|
+
GtDeltaComponent] });
|
|
1047
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.1.1", ngImport: i0, type: GenericTableCoreModule, decorators: [{
|
|
1048
|
+
type: NgModule,
|
|
1049
|
+
args: [{
|
|
1050
|
+
imports: [
|
|
1051
|
+
CommonModule,
|
|
1052
|
+
CoreComponent,
|
|
1053
|
+
SortClassPipe,
|
|
1054
|
+
DashCasePipe,
|
|
1055
|
+
HighlightPipe,
|
|
1056
|
+
RowSelectionPipe,
|
|
1057
|
+
CapitalCasePipe,
|
|
1058
|
+
CapitalCasePipe,
|
|
1059
|
+
DynamicPipe,
|
|
1060
|
+
GtDeltaComponent,
|
|
1061
|
+
],
|
|
1062
|
+
exports: [CoreComponent, GtDeltaComponent],
|
|
1063
|
+
declarations: [],
|
|
1064
|
+
}]
|
|
1065
|
+
}] });
|
|
1066
|
+
|
|
1067
|
+
class PaginationComponent {
|
|
1068
|
+
constructor() {
|
|
1069
|
+
this._table$ = new ReplaySubject(1);
|
|
1070
|
+
this._ariaLabels = {
|
|
1071
|
+
nav: 'Table pagination',
|
|
1072
|
+
button: 'Go to page ',
|
|
1073
|
+
};
|
|
1074
|
+
this._classes = {
|
|
1075
|
+
ul: 'pagination',
|
|
1076
|
+
li: 'page-item',
|
|
1077
|
+
button: 'page-link',
|
|
1078
|
+
};
|
|
1079
|
+
this._paginationLength = 5;
|
|
1080
|
+
/** paginationListItems$ - observable for page numbers to show based on number of pages and current position */
|
|
1081
|
+
this.paginationListItems$ = this._table$.pipe(switchMap((core) => combineLatest([
|
|
1082
|
+
core?.table$.pipe(pluck('info')),
|
|
1083
|
+
core?.currentPaginationIndex$,
|
|
1084
|
+
])), map(([info, currentPage]) => this._generateList(info.pageTotal, currentPage)), shareReplay(1));
|
|
1085
|
+
}
|
|
1086
|
+
get pagingInfo() {
|
|
1087
|
+
return (this._pagingInfo || {
|
|
1088
|
+
pageNext: null,
|
|
1089
|
+
pageCurrent: null,
|
|
1090
|
+
pagePrevious: null,
|
|
1091
|
+
pageSize: null,
|
|
1092
|
+
numberOfRecords: null,
|
|
1093
|
+
pageTotal: null,
|
|
1094
|
+
});
|
|
1095
|
+
}
|
|
1096
|
+
/** pagingInfo
|
|
1097
|
+
* @description when provided, pagination component will use this information to render pagination instead of data from table. Use this option when pagination is handled by backend (server side).
|
|
1098
|
+
* @type info - metadata for pagination component
|
|
1099
|
+
*/
|
|
1100
|
+
set pagingInfo(info) {
|
|
1101
|
+
this._pagingInfo = info;
|
|
1102
|
+
}
|
|
1103
|
+
get paginationLength() {
|
|
1104
|
+
return this._paginationLength;
|
|
1105
|
+
}
|
|
1106
|
+
/** paginationLength
|
|
1107
|
+
* @description number of buttons to show in pagination
|
|
1108
|
+
* @type length - number of buttons to show. Defaults to: `5`
|
|
1109
|
+
*/
|
|
1110
|
+
set paginationLength(length) {
|
|
1111
|
+
this._paginationLength = +length;
|
|
1112
|
+
}
|
|
1113
|
+
get classes() {
|
|
1114
|
+
return this._classes;
|
|
1115
|
+
}
|
|
1116
|
+
/** classes
|
|
1117
|
+
* @description classes that should be used within pagination component for different elements
|
|
1118
|
+
* @type classes - classes to be used. Defaults to: `{
|
|
1119
|
+
* ul: 'pagination',
|
|
1120
|
+
* li: 'page-item',
|
|
1121
|
+
* button: 'page-link',
|
|
1122
|
+
* }`
|
|
1123
|
+
*/
|
|
1124
|
+
set classes(classes) {
|
|
1125
|
+
this._classes = classes;
|
|
1126
|
+
}
|
|
1127
|
+
get ariaLabels() {
|
|
1128
|
+
return this._ariaLabels;
|
|
1129
|
+
}
|
|
1130
|
+
/** ariaLabels
|
|
1131
|
+
* @description aria labels that describe pagination component
|
|
1132
|
+
* @type labels - aria labels for pagination. Defaults to: `{
|
|
1133
|
+
* nav: 'Table pagination',
|
|
1134
|
+
* button: 'Go to page ',
|
|
1135
|
+
* }`
|
|
1136
|
+
*/
|
|
1137
|
+
set ariaLabels(labels) {
|
|
1138
|
+
this._ariaLabels = labels;
|
|
1139
|
+
}
|
|
1140
|
+
get table() {
|
|
1141
|
+
return this._table;
|
|
1142
|
+
}
|
|
1143
|
+
/** table
|
|
1144
|
+
* @description table component to which pagination is attached
|
|
1145
|
+
* @type tableRef - table component
|
|
1146
|
+
*/
|
|
1147
|
+
set table(tableRef) {
|
|
1148
|
+
this._table = tableRef;
|
|
1149
|
+
this._table$.next(tableRef);
|
|
1150
|
+
}
|
|
1151
|
+
/** generate list - generate an array with page numbers to show based on number of pages and current position
|
|
1152
|
+
* @param numberOfPages number of pages to show
|
|
1153
|
+
* @param currentPosition current position (page index) being shown in table
|
|
1154
|
+
* @returns Array<number> array of page numbers to show
|
|
1155
|
+
*/
|
|
1156
|
+
_generateList(numberOfPages, currentPosition) {
|
|
1157
|
+
const middle = Math.floor(this.paginationLength / 2);
|
|
1158
|
+
const length = numberOfPages < this.paginationLength
|
|
1159
|
+
? numberOfPages
|
|
1160
|
+
: this.paginationLength;
|
|
1161
|
+
return Array.from({ length }, (_, i) => {
|
|
1162
|
+
if (i === 0) {
|
|
1163
|
+
return 1;
|
|
1164
|
+
}
|
|
1165
|
+
else if (numberOfPages < this.paginationLength) {
|
|
1166
|
+
return i + 1;
|
|
1167
|
+
}
|
|
1168
|
+
else if (i + 1 === length) {
|
|
1169
|
+
return numberOfPages;
|
|
1170
|
+
}
|
|
1171
|
+
else if (currentPosition > middle &&
|
|
1172
|
+
currentPosition < numberOfPages - middle) {
|
|
1173
|
+
return i + currentPosition - (middle - 1);
|
|
1174
|
+
}
|
|
1175
|
+
else if (currentPosition > middle &&
|
|
1176
|
+
currentPosition < numberOfPages - (middle - 1)) {
|
|
1177
|
+
return i + currentPosition - middle;
|
|
1178
|
+
}
|
|
1179
|
+
else if (currentPosition > middle &&
|
|
1180
|
+
currentPosition === numberOfPages - (middle - 1)) {
|
|
1181
|
+
return i + currentPosition - (middle + 1);
|
|
1182
|
+
}
|
|
1183
|
+
else if (currentPosition > middle &&
|
|
1184
|
+
currentPosition === numberOfPages - 1) {
|
|
1185
|
+
return i + currentPosition - (middle + 2);
|
|
1186
|
+
}
|
|
1187
|
+
else {
|
|
1188
|
+
return i + 1;
|
|
1189
|
+
}
|
|
1190
|
+
});
|
|
1191
|
+
}
|
|
1192
|
+
/** go to page
|
|
1193
|
+
* @param index - page index to go to
|
|
1194
|
+
*/
|
|
1195
|
+
goToPage(index) {
|
|
1196
|
+
if (this.table) {
|
|
1197
|
+
this.table.paginationIndex = index - 1;
|
|
1198
|
+
}
|
|
1199
|
+
}
|
|
1200
|
+
}
|
|
1201
|
+
PaginationComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.1.1", ngImport: i0, type: PaginationComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
1202
|
+
PaginationComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "15.1.1", type: PaginationComponent, isStandalone: true, selector: "angular-generic-table-pagination", inputs: { pagingInfo: "pagingInfo", paginationLength: "paginationLength", classes: "classes", ariaLabels: "ariaLabels", table: "table" }, ngImport: i0, template: "<ng-container\n *ngIf=\"{\n links: paginationListItems$ | async,\n currentPosition: table?.currentPaginationIndex$ | async\n } as pagination\"\n>\n <nav\n *ngIf=\"pagination.links && pagination.links.length > 1\"\n role=\"navigation\"\n [attr.aria-label]=\"ariaLabels.nav\"\n class=\"gt-pagination\"\n [class]=\"classes.nav\"\n >\n <ul [class]=\"classes.ul\">\n <ng-container\n *ngFor=\"\n let position of pagination!.links;\n let i = index;\n let last = last\n \"\n >\n <li\n [class]=\"classes.li\"\n [class.active]=\"position === (pagination!.currentPosition || 0) + 1\"\n >\n <button\n [class]=\"classes.button\"\n [attr.aria-label]=\"ariaLabels.button + position\"\n (click)=\"goToPage(position)\"\n >\n {{ position }}\n </button>\n </li>\n <li\n [class]=\"classes.li\"\n class=\"gt-ellipsis\"\n *ngIf=\"position + 1 !== pagination!.links![i + 1] && !last\"\n >\n <button [class]=\"classes.button\" disabled tabindex=\"-1\"></button>\n </li>\n </ng-container>\n </ul>\n </nav>\n</ng-container>\n", dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "pipe", type: i1.AsyncPipe, name: "async" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
1203
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.1.1", ngImport: i0, type: PaginationComponent, decorators: [{
|
|
1204
|
+
type: Component,
|
|
1205
|
+
args: [{ selector: 'angular-generic-table-pagination', changeDetection: ChangeDetectionStrategy.OnPush, standalone: true, imports: [CommonModule], template: "<ng-container\n *ngIf=\"{\n links: paginationListItems$ | async,\n currentPosition: table?.currentPaginationIndex$ | async\n } as pagination\"\n>\n <nav\n *ngIf=\"pagination.links && pagination.links.length > 1\"\n role=\"navigation\"\n [attr.aria-label]=\"ariaLabels.nav\"\n class=\"gt-pagination\"\n [class]=\"classes.nav\"\n >\n <ul [class]=\"classes.ul\">\n <ng-container\n *ngFor=\"\n let position of pagination!.links;\n let i = index;\n let last = last\n \"\n >\n <li\n [class]=\"classes.li\"\n [class.active]=\"position === (pagination!.currentPosition || 0) + 1\"\n >\n <button\n [class]=\"classes.button\"\n [attr.aria-label]=\"ariaLabels.button + position\"\n (click)=\"goToPage(position)\"\n >\n {{ position }}\n </button>\n </li>\n <li\n [class]=\"classes.li\"\n class=\"gt-ellipsis\"\n *ngIf=\"position + 1 !== pagination!.links![i + 1] && !last\"\n >\n <button [class]=\"classes.button\" disabled tabindex=\"-1\"></button>\n </li>\n </ng-container>\n </ul>\n </nav>\n</ng-container>\n" }]
|
|
1206
|
+
}], propDecorators: { pagingInfo: [{
|
|
1207
|
+
type: Input
|
|
1208
|
+
}], paginationLength: [{
|
|
1209
|
+
type: Input
|
|
1210
|
+
}], classes: [{
|
|
1211
|
+
type: Input
|
|
1212
|
+
}], ariaLabels: [{
|
|
1213
|
+
type: Input
|
|
1214
|
+
}], table: [{
|
|
1215
|
+
type: Input
|
|
1216
|
+
}] } });
|
|
1217
|
+
|
|
1218
|
+
class GenericTablePaginationModule {
|
|
1219
|
+
}
|
|
1220
|
+
GenericTablePaginationModule.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.1.1", ngImport: i0, type: GenericTablePaginationModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule });
|
|
1221
|
+
GenericTablePaginationModule.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "15.1.1", ngImport: i0, type: GenericTablePaginationModule, imports: [CommonModule, PaginationComponent], exports: [PaginationComponent] });
|
|
1222
|
+
GenericTablePaginationModule.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "15.1.1", ngImport: i0, type: GenericTablePaginationModule, imports: [CommonModule, PaginationComponent] });
|
|
1223
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.1.1", ngImport: i0, type: GenericTablePaginationModule, decorators: [{
|
|
1224
|
+
type: NgModule,
|
|
1225
|
+
args: [{
|
|
1226
|
+
imports: [CommonModule, PaginationComponent],
|
|
1227
|
+
exports: [PaginationComponent],
|
|
1228
|
+
}]
|
|
1229
|
+
}] });
|
|
1230
|
+
|
|
1231
|
+
/*
|
|
1232
|
+
* Public API Surface of core
|
|
1233
|
+
*/
|
|
1234
|
+
|
|
1235
|
+
/**
|
|
1236
|
+
* Generated bundle index. Do not edit.
|
|
1237
|
+
*/
|
|
1238
|
+
|
|
1239
|
+
export { CapitalCasePipe, CoreComponent, CoreService, DashCasePipe, DynamicPipe, GenericTableCoreModule, GenericTablePaginationModule, GtDeltaComponent, HighlightPipe, PaginationComponent, SortClassPipe, calculate, capitalize, chunk, dashed, parseSortOrderParams, search, sortOnMultipleKeys, sortOrderConfigToParam, sortOrderToParams };
|
|
1240
|
+
//# sourceMappingURL=angular-generic-table-core.mjs.map
|