@cdmx/wappler_ag_grid 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dmx-ag-grid.js ADDED
@@ -0,0 +1,507 @@
1
+ dmx.Component('ag-grid', {
2
+ initialData: {
3
+ id: null,
4
+ rowData: [],
5
+ column_defs: [],
6
+ cstyles: null,
7
+ gridInstance: null,
8
+ domLayout: 'autoHeight',
9
+ enableCellTextSelection: true,
10
+ rowSelection: null,
11
+ suppressRowDeselection: null,
12
+ pagination: null,
13
+ paginationPageSize: null,
14
+ rowHeight: null,
15
+ headerHeight: null,
16
+ suppressRowClickSelection: null,
17
+ suppressMenuHide: null,
18
+ suppressMovableColumns: null,
19
+ enableCellExpressions: null,
20
+ animateRows: null,
21
+ suppressAggFuncInHeader: null,
22
+ suppressAggAtRootLevel: null,
23
+ suppressClipboardPaste: null,
24
+ suppressScrollOnNewData: null,
25
+ suppressPropertyNamesCheck: null,
26
+ localeText: null,
27
+ minWidth: 150,
28
+ resizable: true,
29
+ sortable: true,
30
+ filter: true,
31
+ floatingFilter: true,
32
+ columnHoverHighlight: true,
33
+ exportToCSV: true,
34
+ fixedHeader: false,
35
+ topbarClass: null,
36
+ fixedHeaderOffset: 100,
37
+ fixedTopOffset: 80
38
+ },
39
+
40
+ attributes: {
41
+ id: {
42
+ default: null
43
+ },
44
+ rowData: {
45
+ type: Array,
46
+ default: []
47
+ },
48
+ column_defs: {
49
+ type: Array,
50
+ default: []
51
+ },
52
+ cstyles: {
53
+ type: Object,
54
+ default: {}
55
+ },
56
+ data: {
57
+ type: Array,
58
+ default: []
59
+ },
60
+ domLayout: {
61
+ default: 'autoHeight'
62
+ },
63
+ enableCellTextSelection: {
64
+ type: Boolean,
65
+ default: true
66
+ },
67
+ rowSelection: {
68
+ type: Boolean,
69
+ default: false
70
+ },
71
+ suppressRowDeselection: {
72
+ type: Boolean,
73
+ default: false
74
+ },
75
+ pagination: {
76
+ type: Boolean,
77
+ default: true
78
+ },
79
+ paginationPageSize: {
80
+ default: 20
81
+ },
82
+ rowHeight: {
83
+ type: Number,
84
+ default: null
85
+ },
86
+ headerHeight: {
87
+ type: Number,
88
+ default: null
89
+ },
90
+ suppressRowClickSelection: {
91
+ type: Boolean,
92
+ default: false
93
+ },
94
+ suppressMenuHide: {
95
+ type: Boolean,
96
+ default: false
97
+ },
98
+ suppressMovableColumns: {
99
+ type: Boolean,
100
+ default: false
101
+ },
102
+ enableCellExpressions: {
103
+ type: Boolean,
104
+ default: false
105
+ },
106
+ animateRows: {
107
+ type: Boolean,
108
+ default: false
109
+ },
110
+ suppressAggFuncInHeader: {
111
+ type: Boolean,
112
+ default: false
113
+ },
114
+ suppressAggAtRootLevel: {
115
+ type: Boolean,
116
+ default: false
117
+ },
118
+ suppressClipboardPaste: {
119
+ type: Boolean,
120
+ default: false
121
+ },
122
+ suppressScrollOnNewData: {
123
+ type: Boolean,
124
+ default: false
125
+ },
126
+ suppressPropertyNamesCheck: {
127
+ type: Boolean,
128
+ default: false
129
+ },
130
+ localeText: {
131
+ default: null
132
+ },
133
+ minWidth: {
134
+ type: Number,
135
+ default: 150
136
+ },
137
+ sortable: {
138
+ type: Boolean,
139
+ default: true
140
+ },
141
+ resizable: {
142
+ type: Boolean,
143
+ default: true
144
+ },
145
+ filter: {
146
+ type: Boolean,
147
+ default: true
148
+ },
149
+ floatingFilter: {
150
+ type: Boolean,
151
+ default: true
152
+ },
153
+ columnHoverHighlight: {
154
+ type: Boolean,
155
+ default: true
156
+ },
157
+ exportToCSV: {
158
+ type: Boolean,
159
+ default: true
160
+ },
161
+ fixedHeader: {
162
+ type: Boolean,
163
+ default: false
164
+ },
165
+ topbarClass: {
166
+ type: Text,
167
+ default: 'topbar'
168
+ },
169
+ fixedHeaderOffset: {
170
+ type: Number,
171
+ default: 100
172
+ },
173
+ fixedTopOffset: {
174
+ type: Number,
175
+ default: 80
176
+ }
177
+ },
178
+
179
+ methods: {
180
+ setData: function (rowData, columnDefs) {
181
+ this.set('rowData', rowData);
182
+ this.set('columnDefs', columnDefs);
183
+ this.refreshGrid();
184
+ }
185
+ },
186
+
187
+ refreshGrid: function () {
188
+ const gridId = this.props.id;
189
+ const rowData = this.props.data;
190
+ const fixedHeader = this.props.fixedHeader;
191
+ const fixedHeaderOffset = this.props.fixedHeaderOffset;
192
+ const topbarClass = this.props.topbarClass;
193
+ const fixedTopOffset = this.props.fixedTopOffset
194
+ let columnDefs = [];
195
+ let exportToCSV = this.props.exportToCSV;
196
+ if (!rowData || rowData.length === 0) {
197
+ console.error('No row data provided.');
198
+ return;
199
+ }
200
+
201
+ function humanize(str) {
202
+ if (str == null) return str;
203
+
204
+ str = String(str)
205
+ .trim()
206
+ .replace(/([a-z\D])([A-Z]+)/g, '$1_$2')
207
+ .replace(/[-\s]+/g, '_')
208
+ .toLowerCase()
209
+ .replace(/_id$/, '')
210
+ .replace(/_/g, ' ')
211
+ .trim();
212
+
213
+ return str.charAt(0).toUpperCase() + str.substr(1);
214
+ }
215
+
216
+ function blankOrNullValueFormatter(params) {
217
+ if (params.value == null) {
218
+ return "-";
219
+ }
220
+
221
+ if (typeof params.value === "string") {
222
+ return params.value.trim() === "" ? "-" : params.value;
223
+ }
224
+
225
+ return params.value;
226
+ }
227
+
228
+ function formatTime(params, timezone) {
229
+ if (params.value == null) {
230
+ return '-';
231
+ } else {
232
+ const date = new Date(params.value);
233
+ if (timezone) {
234
+ const options = {
235
+ day: '2-digit',
236
+ month: '2-digit',
237
+ year: 'numeric',
238
+ hour: 'numeric',
239
+ minute: 'numeric',
240
+ hour12: true,
241
+ timeZone: 'UTC'
242
+ };
243
+ return date.toLocaleString('en-GB', options).toUpperCase();
244
+ } else {
245
+ const options = {
246
+ day: '2-digit',
247
+ month: '2-digit',
248
+ year: 'numeric',
249
+ hour: 'numeric',
250
+ minute: 'numeric',
251
+ hour12: true
252
+ };
253
+ return date.toLocaleString('en-GB', options).toUpperCase();
254
+ }
255
+ }
256
+ }
257
+ // Function to detect the data type based on the values
258
+ function detectDataType(values) {
259
+ let hasDate = false;
260
+ let hasNumber = false;
261
+ let hasText = false;
262
+
263
+ for (const value of values) {
264
+ if (value === null || value === undefined || value === '') {
265
+ hasText = true;
266
+ } else if (!isNaN(Number(value))) {
267
+ hasNumber = true;
268
+ } else if (!isNaN(Date.parse(value))) {
269
+ hasDate = true;
270
+ } else {
271
+ hasText = true;
272
+ }
273
+ }
274
+
275
+ if (hasDate && !hasNumber && !hasText) {
276
+ return 'date';
277
+ } else if (hasNumber && !hasText) {
278
+ return 'number';
279
+ } else {
280
+ return 'text';
281
+ }
282
+ }
283
+ console.log(this.props.cstyles)
284
+ if (Array.isArray(this.props.column_defs) && this.props.column_defs.length > 0) {
285
+ columnDefs = this.props.column_defs;
286
+ } else {
287
+ const firstRow = rowData[0];
288
+
289
+ columnDefs = Object.keys(firstRow).map(key => {
290
+ // Assuming rowData is an array of objects
291
+ const values = rowData.map(row => row[key]);
292
+ const nonNullValues = values.filter(value => value !== null);
293
+ const dataType = detectDataType(nonNullValues);
294
+ let filter;
295
+ let valueGetter;
296
+ let filterValueGetter;
297
+ let valueFormatter;
298
+
299
+
300
+ if (dataType === 'number') {
301
+ filter = 'agNumberColumnFilter';
302
+ if (/(amount|amt)$/.test(key)) {
303
+ valueFormatter = function (params) {
304
+ if (params.value != null) {
305
+ return Number(params.value).toLocaleString("en-US", {
306
+ minimumFractionDigits: 2,
307
+ maximumFractionDigits: 2,
308
+ });
309
+ }
310
+ return '-';
311
+ };
312
+ } else {
313
+ valueFormatter = blankOrNullValueFormatter;
314
+ }
315
+ } else if (dataType === 'date') {
316
+ filter = 'agDateColumnFilter';
317
+ valueFormatter = (params) => formatTime(params, false);
318
+ } else {
319
+ filter = 'agTextColumnFilter';
320
+ valueFormatter = blankOrNullValueFormatter;
321
+ }
322
+ // Check if custom definition exists for the current field
323
+ if (this.props.definitions && this.props.definitions[key]) {
324
+ const definition = this.props.definitions[key];
325
+ if (definition.valueGetter) {
326
+ valueGetter = eval(`(data) => ${definition.valueGetter}`);
327
+ }
328
+ if (definition.filterGetter) {
329
+ filterValueGetter = eval(`(params) => ${definition.filterGetter}`);
330
+ }
331
+ }
332
+ function extractConditionParts(condition) {
333
+ const parts = condition.match(/(.+?)(===|==|!=|>|<|>=|<=)(.+)/);
334
+ if (parts) {
335
+ const [, left, operator, right] = parts;
336
+ return [left.trim(), operator.trim(), right.trim()];
337
+ }
338
+ return [];
339
+ }
340
+
341
+ function evaluateCondition(left, operator, right) {
342
+ switch (operator) {
343
+ case '===':
344
+ return left.toString() === right.toString();
345
+ case '==':
346
+ return left.toString() == right.toString();
347
+ case '!=':
348
+ return left.toString() != right.toString();
349
+ case '>':
350
+ return left.toNumber() > right.toNumber();
351
+ case '<':
352
+ return left.toNumber() < right.toNumber();
353
+ case '>=':
354
+ return left.toNumber() >= right.toNumber();
355
+ case '<=':
356
+ return left.toNumber() <= right.toNumber();
357
+ default:
358
+ return false;
359
+ }
360
+ }
361
+ const cstyles = this.props.cstyles
362
+ // Check if custom color exists for the current field and condition
363
+ function applyCellStyle(params) {
364
+ const field = params.colDef.field.toString();
365
+ if (cstyles.hasOwnProperty(field)) {
366
+ const condition = cstyles[field].condition;
367
+ const customColor = cstyles[field].customColor;
368
+ const [left, operator, right] = extractConditionParts(condition);
369
+ // Check if the field exists and its value is true
370
+ if (params.data.hasOwnProperty(field) && evaluateCondition(params.data[left], operator, right)) {
371
+ return { color: customColor };
372
+ }
373
+ }
374
+ return null;
375
+ }
376
+ return {
377
+ headerName: humanize(key),
378
+ field: key,
379
+ filter: filter,
380
+ valueFormatter: valueFormatter,
381
+ valueGetter: valueGetter,
382
+ filterValueGetter: filterValueGetter,
383
+ cellStyle: applyCellStyle
384
+ };
385
+ });
386
+ }
387
+ const gridOptions = {
388
+ columnDefs: columnDefs,
389
+ defaultColDef: {
390
+ flex: 1,
391
+ minWidth: this.props.minWidth,
392
+ resizable: this.props.resizable,
393
+ filter: this.props.filter,
394
+ sortable: this.props.sortable,
395
+ floatingFilter: this.props.floatingFilter
396
+ },
397
+ domLayout: 'autoHeight',
398
+ enableCellTextSelection: true,
399
+ rowSelection: this.props.rowSelection,
400
+ suppressRowDeselection: this.props.suppressRowDeselection,
401
+ pagination: this.props.pagination,
402
+ paginationPageSize: this.props.paginationPageSize,
403
+ rowHeight: this.props.rowHeight,
404
+ headerHeight: this.props.headerHeight,
405
+ suppressRowClickSelection: this.props.suppressRowClickSelection,
406
+ suppressMenuHide: this.props.suppressMenuHide,
407
+ suppressMovableColumns: this.props.suppressMovableColumns,
408
+ enableCellExpressions: this.props.enableCellExpressions,
409
+ animateRows: this.props.animateRows,
410
+ suppressAggFuncInHeader: this.props.suppressAggFuncInHeader,
411
+ suppressAggAtRootLevel: this.props.suppressAggAtRootLevel,
412
+ suppressClipboardPaste: this.props.suppressClipboardPaste,
413
+ suppressScrollOnNewData: this.props.suppressScrollOnNewData,
414
+ suppressPropertyNamesCheck: this.props.suppressPropertyNamesCheck,
415
+ localeText: this.props.localeText
416
+ };
417
+
418
+ const gridDiv = document.querySelector('.ag-theme-alpine');
419
+
420
+ if (!gridDiv) {
421
+ console.error(`Grid container element with ID '${gridId}' not found.`);
422
+ return;
423
+ }
424
+
425
+ if (this.props.gridInstance) {
426
+ this.props.gridInstance.destroy(); // Destroy the previous grid instance if it exists
427
+ }
428
+ const gridConfig = {
429
+ columnDefs: columnDefs,
430
+ rowData: rowData,
431
+ ...gridOptions
432
+ };
433
+
434
+ // Create ag-Grid instance
435
+ new agGrid.Grid(gridDiv, gridConfig);
436
+ const gridElement = document.querySelector('.ag-theme-alpine');
437
+ const gridContainer = gridElement.parentNode;
438
+
439
+ if (!gridContainer) {
440
+ console.error('Grid container not found.');
441
+ return;
442
+ }
443
+ if (fixedHeader) {
444
+ window.addEventListener('scroll', function () {
445
+ const header = document.querySelector('.ag-header');
446
+ const topbar = document.querySelector('.' + topbarClass);
447
+ const topbarHeight = (topbar ? topbar.getBoundingClientRect().height : 0) + fixedTopOffset;
448
+ const headerPos = (topbar ? topbar.getBoundingClientRect().bottom : 0) + fixedHeaderOffset;
449
+
450
+ if (window.pageYOffset > headerPos) {
451
+ header.style.position = 'fixed';
452
+ header.style.top = `${topbarHeight}px`;
453
+ header.style.zIndex = '1';
454
+ document.body.style.marginBottom = `${header.offsetHeight}px`; // Add margin to the bottom of the page
455
+ } else {
456
+ header.style.position = 'static';
457
+ document.body.style.marginBottom = '0'; // Reset the margin
458
+ }
459
+ });
460
+ }
461
+
462
+ // Create the export button
463
+ if (exportToCSV) {
464
+ const exportButton = document.createElement('button');
465
+ exportButton.innerText = 'Export to CSV';
466
+ exportButton.addEventListener('click', () => {
467
+ const params = {
468
+ fileName: 'export.csv', // Set the desired file name here
469
+ allColumns: true,
470
+ processCellCallback: function (params) {
471
+ const columnDef = params.column.getColDef();
472
+ const valueFormatter = columnDef.valueFormatter;
473
+ if (valueFormatter && typeof valueFormatter === "function") {
474
+ const formattedValue = valueFormatter(params);
475
+ if (formattedValue !== null && formattedValue !== undefined) {
476
+ return formattedValue;
477
+ }
478
+ }
479
+ return params.value;
480
+ },
481
+ };
482
+ gridConfig.api.exportDataAsCsv(params);
483
+ });
484
+
485
+ // Append the export button to the grid container
486
+ gridContainer.parentNode.insertBefore(exportButton, gridContainer);
487
+
488
+ exportButton.style.marginBottom = '10px';
489
+ }
490
+ },
491
+
492
+ events: {
493
+ 'dmx-ag-grid-row-data-updated': Event
494
+ },
495
+
496
+ render: function () {
497
+ // this.refreshGrid();
498
+ },
499
+
500
+ update: function (props) {
501
+ // dmx.equal is a helper function the does a deep compare
502
+ // which is useful when comparing arrays and objects
503
+ if (!dmx.equal(this.props.data, props.data)) {
504
+ this.refreshGrid();
505
+ }
506
+ },
507
+ });
package/package.json ADDED
@@ -0,0 +1,20 @@
1
+ {
2
+ "name": "@cdmx/wappler_ag_grid",
3
+ "version": "0.0.1",
4
+ "type": "module",
5
+ "description": "App Connect module for AG Grid Table Generation",
6
+ "license": "MIT",
7
+ "author": {
8
+ "name": "cdmx1",
9
+ "url": "https://github.com/cdmx1"
10
+ },
11
+ "keywords": [
12
+ "wappler-extension",
13
+ "app-connect",
14
+ "ag-grid"
15
+ ],
16
+ "scripts": {
17
+ "build": "rollup --config",
18
+ "publish-dry-run": "npm publish ./dist --access public --dry-run"
19
+ }
20
+ }