ngx-deebodata 0.1.2 → 0.1.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +37 -2
- package/fesm2022/ngx-deebodata.mjs +6969 -0
- package/fesm2022/ngx-deebodata.mjs.map +1 -0
- package/package.json +41 -18
- package/types/ngx-deebodata.d.ts +460 -0
- package/ng-package.json +0 -7
- package/src/lib/data-table/charts/bar-graph-component/bar-graph-component.css +0 -148
- package/src/lib/data-table/charts/bar-graph-component/bar-graph-component.html +0 -29
- package/src/lib/data-table/charts/bar-graph-component/bar-graph-component.spec.ts +0 -23
- package/src/lib/data-table/charts/bar-graph-component/bar-graph-component.ts +0 -282
- package/src/lib/data-table/charts/charts-and-graphs/charts-and-graphs.css +0 -27
- package/src/lib/data-table/charts/charts-and-graphs/charts-and-graphs.html +0 -53
- package/src/lib/data-table/charts/charts-and-graphs/charts-and-graphs.spec.ts +0 -23
- package/src/lib/data-table/charts/charts-and-graphs/charts-and-graphs.ts +0 -214
- package/src/lib/data-table/charts/column-chart/column-chart.css +0 -19
- package/src/lib/data-table/charts/column-chart/column-chart.html +0 -47
- package/src/lib/data-table/charts/column-chart/column-chart.spec.ts +0 -23
- package/src/lib/data-table/charts/column-chart/column-chart.ts +0 -178
- package/src/lib/data-table/charts/line-graph-component/line-graph-component.css +0 -25
- package/src/lib/data-table/charts/line-graph-component/line-graph-component.html +0 -59
- package/src/lib/data-table/charts/line-graph-component/line-graph-component.spec.ts +0 -23
- package/src/lib/data-table/charts/line-graph-component/line-graph-component.ts +0 -661
- package/src/lib/data-table/charts/num-value-distro-component/num-value-distro-component.css +0 -53
- package/src/lib/data-table/charts/num-value-distro-component/num-value-distro-component.html +0 -27
- package/src/lib/data-table/charts/num-value-distro-component/num-value-distro-component.spec.ts +0 -23
- package/src/lib/data-table/charts/num-value-distro-component/num-value-distro-component.ts +0 -210
- package/src/lib/data-table/charts/pie-graph-component/pie-graph-component.css +0 -0
- package/src/lib/data-table/charts/pie-graph-component/pie-graph-component.html +0 -15
- package/src/lib/data-table/charts/pie-graph-component/pie-graph-component.spec.ts +0 -23
- package/src/lib/data-table/charts/pie-graph-component/pie-graph-component.ts +0 -197
- package/src/lib/data-table/data-table-module/data-cell/data-cell.css +0 -0
- package/src/lib/data-table/data-table-module/data-cell/data-cell.html +0 -6
- package/src/lib/data-table/data-table-module/data-cell/data-cell.spec.ts +0 -23
- package/src/lib/data-table/data-table-module/data-cell/data-cell.ts +0 -298
- package/src/lib/data-table/data-table-module/data-table-header/data-table-header.css +0 -25
- package/src/lib/data-table/data-table-module/data-table-header/data-table-header.html +0 -55
- package/src/lib/data-table/data-table-module/data-table-header/data-table-header.spec.ts +0 -23
- package/src/lib/data-table/data-table-module/data-table-header/data-table-header.ts +0 -261
- package/src/lib/data-table/data-table-module/data-table-paginator/data-table-paginator.css +0 -61
- package/src/lib/data-table/data-table-module/data-table-paginator/data-table-paginator.html +0 -24
- package/src/lib/data-table/data-table-module/data-table-paginator/data-table-paginator.spec.ts +0 -23
- package/src/lib/data-table/data-table-module/data-table-paginator/data-table-paginator.ts +0 -125
- package/src/lib/data-table/data-table-module/export-component/export-component.css +0 -83
- package/src/lib/data-table/data-table-module/export-component/export-component.html +0 -16
- package/src/lib/data-table/data-table-module/export-component/export-component.spec.ts +0 -23
- package/src/lib/data-table/data-table-module/export-component/export-component.ts +0 -206
- package/src/lib/data-table/data-table-module/ngx-deebodata/ngx-deebodata.css +0 -92
- package/src/lib/data-table/data-table-module/ngx-deebodata/ngx-deebodata.html +0 -194
- package/src/lib/data-table/data-table-module/ngx-deebodata/ngx-deebodata.spec.ts +0 -23
- package/src/lib/data-table/data-table-module/ngx-deebodata/ngx-deebodata.ts +0 -2255
- package/src/lib/data-table/data-table-module/row-group-menu/row-group-menu.css +0 -14
- package/src/lib/data-table/data-table-module/row-group-menu/row-group-menu.html +0 -27
- package/src/lib/data-table/data-table-module/row-group-menu/row-group-menu.spec.ts +0 -23
- package/src/lib/data-table/data-table-module/row-group-menu/row-group-menu.ts +0 -58
- package/src/lib/data-table/data-table-module/row-group-panel/row-group-panel.css +0 -15
- package/src/lib/data-table/data-table-module/row-group-panel/row-group-panel.html +0 -48
- package/src/lib/data-table/data-table-module/row-group-panel/row-group-panel.spec.ts +0 -23
- package/src/lib/data-table/data-table-module/row-group-panel/row-group-panel.ts +0 -599
- package/src/lib/data-table/data-table-module/worker.worker.ts +0 -44
- package/src/lib/interfaces/cell-edit.ts +0 -7
- package/src/lib/interfaces/column-header.ts +0 -10
- package/src/lib/interfaces/column-styles.ts +0 -14
- package/src/lib/interfaces/column-symbol.ts +0 -4
- package/src/lib/interfaces/data-cell.ts +0 -14
- package/src/lib/interfaces/data-row.ts +0 -11
- package/src/lib/interfaces/row-number.ts +0 -4
- package/src/lib/services/common-service.spec.ts +0 -16
- package/src/lib/services/common-service.ts +0 -336
- package/src/lib/services/data-table-service.spec.ts +0 -16
- package/src/lib/services/data-table-service.ts +0 -605
- package/src/lib/services/table-drag-service.spec.ts +0 -16
- package/src/lib/services/table-drag-service.ts +0 -347
- package/src/lib/styles.css +0 -1068
- package/src/public-api.ts +0 -8
- package/tsconfig.lib.json +0 -17
- package/tsconfig.lib.prod.json +0 -11
- package/tsconfig.spec.json +0 -15
|
@@ -1,661 +0,0 @@
|
|
|
1
|
-
import { Component, ElementRef, EventEmitter, HostListener, Input, NgZone, Output, signal, ViewChild } from '@angular/core';
|
|
2
|
-
import { CommonService } from '../../../services/common-service';
|
|
3
|
-
import { DataTableService } from '../../../services/data-table-service';
|
|
4
|
-
import { CommonModule } from '@angular/common';
|
|
5
|
-
import { FormsModule } from '@angular/forms';
|
|
6
|
-
|
|
7
|
-
@Component({
|
|
8
|
-
selector: 'app-line-graph-component',
|
|
9
|
-
imports: [ CommonModule, FormsModule ],
|
|
10
|
-
templateUrl: './line-graph-component.html',
|
|
11
|
-
styleUrls: ['./line-graph-component.css', '../bar-graph-component/bar-graph-component.css', '../../../styles.css']
|
|
12
|
-
})
|
|
13
|
-
export class LineGraphComponent {
|
|
14
|
-
|
|
15
|
-
@HostListener('window:resize', ['$event'])
|
|
16
|
-
onWindowResize(e: Event) {
|
|
17
|
-
this.handleStatChange(this.selStat, true)
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
alloneyr: boolean = false
|
|
21
|
-
lblTxt: string = ""
|
|
22
|
-
naddedtks = signal<any[]>([])
|
|
23
|
-
nmrks: any[] = []
|
|
24
|
-
dtmrks: any[] = []
|
|
25
|
-
dtmrksH: any[] = []
|
|
26
|
-
plots = signal<any[]>([])
|
|
27
|
-
contScrollHgt: number = 0
|
|
28
|
-
buildingStat: boolean = false
|
|
29
|
-
maxYVal: string = "0";
|
|
30
|
-
statData: any[] = []
|
|
31
|
-
valOpts = signal<any[]>([])
|
|
32
|
-
selStat: string = "avg"
|
|
33
|
-
statBtnWid: string = "30px"
|
|
34
|
-
titleTrail: string = ""
|
|
35
|
-
useData: any[] = []
|
|
36
|
-
uDates: any[] = []
|
|
37
|
-
selDdVal: string = ""
|
|
38
|
-
selTDOpt: number = -1
|
|
39
|
-
numYPlots: number = 5
|
|
40
|
-
bgDepVarHgt: number = 70//height of y ticks
|
|
41
|
-
totalHgt: number = 297;
|
|
42
|
-
graphMsg: string = "Loading..."
|
|
43
|
-
currentDelta: string = ""
|
|
44
|
-
currentDeltaCls: string = "neutral"
|
|
45
|
-
timeDiffOpts: any[] = []
|
|
46
|
-
statOpts: string[] = ["avg", "min", "max", "mode", "median", "sum"]
|
|
47
|
-
@Input() data: any[] = []
|
|
48
|
-
@Input() numCol: string = ""
|
|
49
|
-
@Input() column: string = ""
|
|
50
|
-
@Output("title") title: EventEmitter<string> = new EventEmitter();
|
|
51
|
-
@ViewChild("yAxis", { static: true }) yAxis!: ElementRef<HTMLDivElement>;
|
|
52
|
-
@ViewChild("lineGraph", { static: true }) lineGraph!: ElementRef<HTMLDivElement>;
|
|
53
|
-
@ViewChild("lgContain", { static: true }) lgContain!: ElementRef<HTMLDivElement>;
|
|
54
|
-
@ViewChild("auxOptsCont", { static: true }) auxOptsCont!: ElementRef<HTMLDivElement>;
|
|
55
|
-
@ViewChild("statOptsCont", { static: true }) statOptsCont!: ElementRef<HTMLDivElement>;
|
|
56
|
-
@ViewChild("lgCanvas", { static: true }) lgCanvas!: ElementRef<HTMLCanvasElement>;
|
|
57
|
-
|
|
58
|
-
constructor(public common: CommonService,
|
|
59
|
-
public dataTableService: DataTableService,
|
|
60
|
-
private _zone: NgZone,)
|
|
61
|
-
{}
|
|
62
|
-
|
|
63
|
-
ngOnInit() {
|
|
64
|
-
const cols = this.column.split(this.dataTableService.bgSep)
|
|
65
|
-
this.totalHgt = ((this.bgDepVarHgt*(this.numYPlots-1))+17);
|
|
66
|
-
if(cols.length === 3){
|
|
67
|
-
this.titleTrail = this.common.titleCase(cols[1]) + " by " + this.common.titleCase(cols[0]);
|
|
68
|
-
} else {
|
|
69
|
-
if(this.numCol)
|
|
70
|
-
this.titleTrail = this.common.titleCase(this.numCol) + " by " + this.common.titleCase(cols[0]);
|
|
71
|
-
}
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
ngAfterContentInit() {
|
|
75
|
-
this.buildLg()
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
buildLg() {
|
|
79
|
-
let valData: any[] = []
|
|
80
|
-
const strODtCol = this.column.split(this.dataTableService.bgSep)[0]
|
|
81
|
-
if(this.dataTableService.dataFilSrtTracker[strODtCol].selDDVals && this.dataTableService.dataFilSrtTracker[strODtCol].selDDVals.length){
|
|
82
|
-
const vals = this.dataTableService.dataFilSrtTracker[strODtCol].selDDVals?.filter( (g: any) => g.checked && g.value !== "(Select All)")
|
|
83
|
-
this.valOpts.set(vals.map( (v: any) => v.value))
|
|
84
|
-
if(!this.selDdVal)
|
|
85
|
-
this.selDdVal = vals[0].value
|
|
86
|
-
valData = this.data.filter( d => d["strColumn"] === this.selDdVal)
|
|
87
|
-
} else {
|
|
88
|
-
valData = this.data.filter( d => true)
|
|
89
|
-
}
|
|
90
|
-
if(this.selTDOpt > -1){//not all time
|
|
91
|
-
try{
|
|
92
|
-
const oneDay = (1000*60*60*24);
|
|
93
|
-
let oData = valData.filter( v => v && v["dtColumn"] && this.common.isADateObject(v["dtColumn"]) && v["dtColumn"].toString() !== "Invalid Date").
|
|
94
|
-
sort( (a, b) => a["dtColumn"] > b["dtColumn"] ? 1 : -1 );
|
|
95
|
-
const vlen = oData.length
|
|
96
|
-
const lastDate = oData[(vlen-1)].dtColumn.getTime()
|
|
97
|
-
const cutoff = (lastDate-(this.selTDOpt*oneDay))
|
|
98
|
-
valData = oData.filter( v => v["dtColumn"].getTime() >= cutoff )
|
|
99
|
-
}catch(e){}
|
|
100
|
-
}
|
|
101
|
-
const dSet = new Set(valData.filter( v => true).sort( (a, b) => a["dtColumn"] > b["dtColumn"] ? 1 : -1 ).map( (d) => JSON.stringify(d["dtColumn"]) ))//unique dates
|
|
102
|
-
dSet.forEach( (v) => {
|
|
103
|
-
const pdt = JSON.parse(v)
|
|
104
|
-
if(typeof pdt === "string")
|
|
105
|
-
this.uDates.push(this.common.coerceDate(pdt))
|
|
106
|
-
else
|
|
107
|
-
this.uDates.push(pdt)
|
|
108
|
-
});
|
|
109
|
-
this.setUseableData(valData, this.selStat)
|
|
110
|
-
setTimeout( () => {
|
|
111
|
-
this.drawLineGraph(this.useData);
|
|
112
|
-
this._zone.run( () => { this.buildingStat = false } );
|
|
113
|
-
})
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
getYTDNumDays(dtmax: any) {
|
|
117
|
-
try{
|
|
118
|
-
const dt = new Date()
|
|
119
|
-
const yr = dt.getFullYear()
|
|
120
|
-
const oneDay = (1000*60*60*24);
|
|
121
|
-
const jan1 = new Date("January 1, "+ yr +" 00:01:00")
|
|
122
|
-
const gap = Math.floor((dtmax.getTime() - jan1.getTime()) / oneDay)
|
|
123
|
-
return gap > 0 ? gap : null;
|
|
124
|
-
} catch(e){ return null}
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
findTimeDiffOpts(milRange: number, dtmax: any) {
|
|
128
|
-
let i = 0
|
|
129
|
-
this.timeDiffOpts = []
|
|
130
|
-
// const oneDay = (1000*60*60*24);
|
|
131
|
-
// const range = Math.floor(milRange/oneDay)
|
|
132
|
-
const ytd = "YTD"
|
|
133
|
-
const ytdNum = this.getYTDNumDays(dtmax)
|
|
134
|
-
let avail: any[] = [{text: "1D", val: 1}, {text: "5D", val: 5},{text: "1M", val: 31},
|
|
135
|
-
{text: "6M", val: Math.ceil(365/2)},{text: "1Y", val: 365}, {text: ytd, val: ytdNum},
|
|
136
|
-
{text: "5Y", val: ((365*5)+1)}];
|
|
137
|
-
if(!ytdNum)
|
|
138
|
-
avail = avail.filter( a => a.text !== ytd)
|
|
139
|
-
const len = avail.length
|
|
140
|
-
for(i; i < len; i++){
|
|
141
|
-
const av = avail[i]
|
|
142
|
-
// if(range > av.val)
|
|
143
|
-
this.timeDiffOpts.push(av)
|
|
144
|
-
}
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
handleStatChange(stat: string, resize?: boolean) {
|
|
148
|
-
if(this.selStat === stat && !resize)
|
|
149
|
-
return;
|
|
150
|
-
this.killPlots("Loading...")
|
|
151
|
-
this.buildingStat = true
|
|
152
|
-
this.maxYVal = "0"
|
|
153
|
-
this.selStat = stat
|
|
154
|
-
if(!resize)
|
|
155
|
-
this.title.emit(this.common.titleCase(stat) + " " + this.titleTrail);
|
|
156
|
-
setTimeout( () => { this.buildLg() })
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
handleValSelChange(selVal: string) {
|
|
160
|
-
this.killPlots("Loading...")
|
|
161
|
-
this.buildingStat = true
|
|
162
|
-
this.selDdVal = selVal
|
|
163
|
-
this.maxYVal = "0"
|
|
164
|
-
setTimeout( () => { this.buildLg() })
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
handleTimeDiffChange(topt: number) {
|
|
168
|
-
this.killPlots("Loading...")
|
|
169
|
-
this.buildingStat = true
|
|
170
|
-
this.selTDOpt = topt
|
|
171
|
-
this.maxYVal = "0"
|
|
172
|
-
setTimeout( () => { this.buildLg() })
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
setUseableData(valArr: any[], stat: string) {
|
|
176
|
-
const dlen = this.uDates.length
|
|
177
|
-
this.useData = []
|
|
178
|
-
let d = 0;
|
|
179
|
-
for(d; d < dlen; d++){
|
|
180
|
-
let obj = {}
|
|
181
|
-
const dt = this.uDates[d]
|
|
182
|
-
const justVal = valArr.filter( (d) => {
|
|
183
|
-
return d && d[("dtColumn")] && this.common.isADateObject(d[("dtColumn")]) && d["dtColumn"].toString() !== "Invalid Date" &&
|
|
184
|
-
this.common.isADateObject(dt) && dt.toString() !== "Invalid Date" && d[("dtColumn")].getTime() === dt.getTime()} ).
|
|
185
|
-
map( function(d) {return d[("numColumn")]} )
|
|
186
|
-
const jlen = justVal.length
|
|
187
|
-
if(jlen){
|
|
188
|
-
switch(stat){
|
|
189
|
-
case "avg":
|
|
190
|
-
obj = { dtColumn: dt, numColumn: (justVal.reduce( (acc, curr) => (acc += curr) , 0)/jlen) }
|
|
191
|
-
break;
|
|
192
|
-
case "min":
|
|
193
|
-
obj = { dtColumn: dt, numColumn: (Math.min(...justVal)) }
|
|
194
|
-
break;
|
|
195
|
-
case "max":
|
|
196
|
-
obj = { dtColumn: dt, numColumn: (Math.max(...justVal)) }
|
|
197
|
-
break;
|
|
198
|
-
case "sum":
|
|
199
|
-
obj = { dtColumn: dt, numColumn: justVal.reduce( (acc, curr) => (acc += curr) , 0) }
|
|
200
|
-
break;
|
|
201
|
-
case "median":
|
|
202
|
-
obj = { dtColumn: dt, numColumn: this.findDataMedian(justVal, jlen) }
|
|
203
|
-
break;
|
|
204
|
-
case "mode":
|
|
205
|
-
obj = { dtColumn: dt, numColumn: this.findDataMode(justVal) }
|
|
206
|
-
break;
|
|
207
|
-
default: "avg"
|
|
208
|
-
obj = { dtColumn: dt, numColumn: (justVal.reduce( (acc, curr) => (acc += curr) , 0)/jlen) };
|
|
209
|
-
}
|
|
210
|
-
this.useData.push(obj)
|
|
211
|
-
}
|
|
212
|
-
}
|
|
213
|
-
}
|
|
214
|
-
|
|
215
|
-
sizeUpBgDims() {
|
|
216
|
-
const bgWid = this.lgContain.nativeElement.getBoundingClientRect().width
|
|
217
|
-
const yWid = this.yAxis.nativeElement.getBoundingClientRect().width
|
|
218
|
-
const offY = Math.ceil(yWid+5)
|
|
219
|
-
this.statBtnWid = Math.floor((bgWid-offY)/this.statOpts.length) + "px";
|
|
220
|
-
this.statOptsCont.nativeElement.style.marginLeft = offY + "px";
|
|
221
|
-
this.auxOptsCont.nativeElement.style.marginLeft = offY + "px";
|
|
222
|
-
}
|
|
223
|
-
|
|
224
|
-
drawLineGraph(data: any[]): any {
|
|
225
|
-
if(data && data.length){
|
|
226
|
-
try{
|
|
227
|
-
const lmarg = 60
|
|
228
|
-
const dlen = data.length
|
|
229
|
-
const lg = this.lineGraph.nativeElement
|
|
230
|
-
const par = this.lgContain.nativeElement
|
|
231
|
-
const canvas = this.lgCanvas.nativeElement
|
|
232
|
-
lg.style.width = ((par.getBoundingClientRect().width*0.92) -lmarg) + "px";
|
|
233
|
-
canvas.width = ((par.getBoundingClientRect().width*0.92) -lmarg)
|
|
234
|
-
const dtopts = {year: "2-digit",month: "2-digit", day: "numeric"}
|
|
235
|
-
const numCol = this.numCol || this.column.split(this.dataTableService.bgSep)[1]
|
|
236
|
-
let lbly = this.common.sanitizeUi(numCol)
|
|
237
|
-
const sym = this.dataTableService.dataFilSrtTracker[numCol].colCellSymbol
|
|
238
|
-
if(sym)
|
|
239
|
-
lbly += " ("+sym+")";
|
|
240
|
-
par.setAttribute("data-yaxis", lbly)
|
|
241
|
-
|
|
242
|
-
//numbers
|
|
243
|
-
const numArr = data.map( (d) => d["numColumn"] ).
|
|
244
|
-
filter( (a) => !!a && a >= 0 ).sort( (a, b) => (a - b)*-1 );
|
|
245
|
-
if(numArr.length < 2 || !numArr.some( (n) => n > 0 ))
|
|
246
|
-
return this.killPlots("Not enough data for a graph")
|
|
247
|
-
const nlen = numArr.length;
|
|
248
|
-
const nmmin = numArr[(nlen-1)]
|
|
249
|
-
const nmmax = numArr[0]
|
|
250
|
-
const nmrange = nmmax - nmmin;
|
|
251
|
-
const nqtrdts = (nmrange/4) + nmmin
|
|
252
|
-
const nhalfdts = (nmrange/2) + nmmin
|
|
253
|
-
const nthrqtrdts = (nmrange*0.75) + nmmin
|
|
254
|
-
const nokTickVal = [nmmax, nthrqtrdts, nhalfdts, nqtrdts, nmmin]//desc order!
|
|
255
|
-
const ntklen = nokTickVal.length
|
|
256
|
-
let nadded = [];
|
|
257
|
-
let mckNaTs: any[] = []
|
|
258
|
-
let t = 0
|
|
259
|
-
this.maxYVal = nmmax < 1000 ? nmmax.toLocaleString(undefined, {maximumFractionDigits: 0}) : this.common.doBigData(nmmax);
|
|
260
|
-
for(t; t < ntklen; t++){
|
|
261
|
-
const val = nokTickVal[t]
|
|
262
|
-
const ntkval: string = val < 1000 ? val.toLocaleString(undefined, {maximumFractionDigits: 0}) : this.common.doBigData(val);
|
|
263
|
-
if(mckNaTs.map( n => n.value).indexOf(ntkval) > -1)
|
|
264
|
-
continue
|
|
265
|
-
mckNaTs.push({value: ntkval, visible: false})
|
|
266
|
-
}
|
|
267
|
-
this.naddedtks.set([...mckNaTs])
|
|
268
|
-
let i = 0
|
|
269
|
-
for(i; i < nlen; i++){//not seen but heard
|
|
270
|
-
const unum = numArr[i]
|
|
271
|
-
if(nadded.indexOf(unum) > -1)
|
|
272
|
-
continue
|
|
273
|
-
const pct = (Math.max(1, unum-nmmin) / Math.max(1, nmrange))
|
|
274
|
-
const usenval = unum?.toLocaleString(undefined, {maximumFractionDigits: 2});
|
|
275
|
-
this.nmrks.push({value: usenval, percent: pct.toFixed(2), visible: false})
|
|
276
|
-
nadded.push(usenval)
|
|
277
|
-
}
|
|
278
|
-
//numbers
|
|
279
|
-
|
|
280
|
-
//dates
|
|
281
|
-
let arrFrmSet: any[] = []
|
|
282
|
-
const dSet = new Set(data.map( (d) => d["dtColumn"] ))//unique dates
|
|
283
|
-
dSet.forEach( (v) => { arrFrmSet.push(v) });
|
|
284
|
-
if(arrFrmSet.length < 2)
|
|
285
|
-
return this.killPlots("Not enough data for a graph");
|
|
286
|
-
const dttype = typeof arrFrmSet[0];
|
|
287
|
-
const ordDts = arrFrmSet.filter( (a) => !!a ).sort( (a, b) => {
|
|
288
|
-
return (dttype !== "object" ? (parseInt(a.toString()) - parseInt(b.toString())) : ( a.getTime() - b.getTime() ))
|
|
289
|
-
})
|
|
290
|
-
let m = 0
|
|
291
|
-
let added = []
|
|
292
|
-
const olen = ordDts.length
|
|
293
|
-
const yearvals = ordDts.map( (o) => typeof o !== "object" ? o : o.getFullYear() )
|
|
294
|
-
this.alloneyr = (new Set(yearvals.filter( (d) => true )).size < 2) ? true : false;//unique years
|
|
295
|
-
// let yrReg = new RegExp("sjdkgf", "g")//not gonna replace anything
|
|
296
|
-
// if(this.alloneyr)
|
|
297
|
-
// yrReg = new RegExp("\/" + yearvals[0], "g")
|
|
298
|
-
let dtrange
|
|
299
|
-
let dtmin: any; let dtmax: any;
|
|
300
|
-
if(typeof ordDts[0] === "object"){
|
|
301
|
-
dtmin = ordDts[0]
|
|
302
|
-
dtmax = ordDts[(ordDts.length-1)]
|
|
303
|
-
}
|
|
304
|
-
if(typeof ordDts[0] === "number"){
|
|
305
|
-
dtmin = ordDts[0]
|
|
306
|
-
dtmax = ordDts[(ordDts.length-1)]
|
|
307
|
-
}
|
|
308
|
-
if(typeof ordDts[0] === "string" && !isNaN(parseInt(ordDts[0])) && !isNaN(parseInt(ordDts[(ordDts.length-1)]))){
|
|
309
|
-
dtmin = parseInt(ordDts[0]);
|
|
310
|
-
dtmax = parseInt(ordDts[(ordDts.length-1)])
|
|
311
|
-
}
|
|
312
|
-
dtrange = typeof ordDts[0] === "object" ? (dtmax.getTime() - dtmin.getTime()) : (dtmax - dtmin)
|
|
313
|
-
if(!dtrange || typeof dtrange !== "number")
|
|
314
|
-
return this.killPlots("Not enough data for a graph");
|
|
315
|
-
if(this.selTDOpt === -1)
|
|
316
|
-
setTimeout( () => { this.findTimeDiffOpts(dtrange, dtmax) }, 500)
|
|
317
|
-
const figureDtTicks = (val: number) => (typeof dtmin === "object" ? new Date(val+dtmin.getTime()) : (val+dtmin))
|
|
318
|
-
const halfdts = figureDtTicks(Math.ceil(dtrange/2))
|
|
319
|
-
const okDtTks = [dtmin, halfdts, dtmax];
|
|
320
|
-
let de = 0
|
|
321
|
-
const dtklen = okDtTks.length
|
|
322
|
-
for(de; de < dtklen; de++){
|
|
323
|
-
const tval = okDtTks[de]
|
|
324
|
-
const useDVal = this.common.isADateObject(tval) ? tval.toLocaleDateString('en-US', dtopts)/*.replace(yrReg, "")*/ : tval
|
|
325
|
-
if(this.dtmrks.find( d => d.value === useDVal))
|
|
326
|
-
continue;
|
|
327
|
-
this.dtmrks.push({value: useDVal, left: 0, visible: false})
|
|
328
|
-
}
|
|
329
|
-
const oZ = ordDts[0].getTime()
|
|
330
|
-
for(m; m < olen; m++){
|
|
331
|
-
const oval = ordDts[m]
|
|
332
|
-
const useDVal = this.common.isADateObject(oval) ? oval.toLocaleDateString('en-US', dtopts)/*.replace(yrReg, "")*/ : oval
|
|
333
|
-
if(added.indexOf(useDVal) > -1)
|
|
334
|
-
continue
|
|
335
|
-
let pct: number = 0
|
|
336
|
-
if(typeof ordDts[0] === "object")
|
|
337
|
-
pct = ((ordDts[m].getTime()-oZ) / dtrange)
|
|
338
|
-
if(typeof ordDts[0] === "number")
|
|
339
|
-
pct = ((ordDts[m]-ordDts[0]) / dtrange)
|
|
340
|
-
if(typeof ordDts[0] === "string")
|
|
341
|
-
pct = ((parseInt(ordDts[m])-parseInt(ordDts[0])) / dtrange)
|
|
342
|
-
this.dtmrksH.push({ value: useDVal, percent: pct, left: 0, visible: false })
|
|
343
|
-
added.push(useDVal)
|
|
344
|
-
}//space out the markers
|
|
345
|
-
//dates
|
|
346
|
-
//set graph dims
|
|
347
|
-
setTimeout( (): any => {
|
|
348
|
-
let r =0
|
|
349
|
-
let hd =0
|
|
350
|
-
const halfPlot = 2
|
|
351
|
-
let lgbds = lg.getBoundingClientRect()
|
|
352
|
-
const mrks = document.querySelectorAll("#" + par.id + " .lg-dt-marker")
|
|
353
|
-
const mrlen = mrks.length
|
|
354
|
-
const hmrlen = this.dtmrksH.length
|
|
355
|
-
// const lastdtdv = mrks[mrlen-1].getBoundingClientRect()
|
|
356
|
-
const space = lgbds.width//(lgbds.right - (lastdtdv.right/*-lmarg*/)) - halfPlot
|
|
357
|
-
const divby = Math.max(1, (mrlen-1))
|
|
358
|
-
const mrrgt = Math.ceil(space/divby)//divide space up by count-1 because last gets no marg right
|
|
359
|
-
for(r; r < mrlen; r++){
|
|
360
|
-
const dtmrk = this.dtmrks[r]
|
|
361
|
-
if(dtmrk)
|
|
362
|
-
dtmrk.left = (Math.floor(mrrgt*r)-halfPlot) + "px";
|
|
363
|
-
}
|
|
364
|
-
setTimeout( () => {
|
|
365
|
-
lgbds = lg.getBoundingClientRect()
|
|
366
|
-
const xrange = space
|
|
367
|
-
for(hd; hd < hmrlen; hd++){
|
|
368
|
-
const hmrk = this.dtmrksH[hd]
|
|
369
|
-
if(hmrk)
|
|
370
|
-
hmrk.left = Math.floor((hmrk.percent*xrange)) + "px";
|
|
371
|
-
}
|
|
372
|
-
|
|
373
|
-
const nmrks = document.querySelectorAll("#" + par.id + " .bg-dep-var")
|
|
374
|
-
const nmrlen = nmrks.length
|
|
375
|
-
const hnmrks = this.nmrks
|
|
376
|
-
const hnmrlen = hnmrks.length
|
|
377
|
-
//set graph dims
|
|
378
|
-
//add x label
|
|
379
|
-
let colsspl = this.column.split(this.dataTableService.bgSep)
|
|
380
|
-
const useCol = colsspl.length === 3 ? colsspl[2] : colsspl[0]
|
|
381
|
-
const pretXlbl = this.common.sanitizeUi(this.common.titleCase(useCol))
|
|
382
|
-
this.lblTxt = pretXlbl
|
|
383
|
-
if(this.alloneyr)
|
|
384
|
-
this.lblTxt = yearvals[0] + " " + pretXlbl
|
|
385
|
-
//add x label
|
|
386
|
-
setTimeout( () => {
|
|
387
|
-
//add plots
|
|
388
|
-
lgbds = lg.getBoundingClientRect()
|
|
389
|
-
const maxPtLft = xrange - halfPlot
|
|
390
|
-
const maxPtBot = lgbds.height - (halfPlot*6) - 1;
|
|
391
|
-
// const dtmrWid =mrks[0].getBoundingClientRect().width
|
|
392
|
-
const findDtLft = (dt: any) => {
|
|
393
|
-
let q = 0; let k = 0
|
|
394
|
-
const ptval = this.common.isADateObject(dt) ? dt.toLocaleDateString('en-US', dtopts)/*.replace(yrReg, "")*/ : dt
|
|
395
|
-
for(q; q < mrlen; q++){
|
|
396
|
-
const dtmrk = this.dtmrks[q]
|
|
397
|
-
if(dtmrk){
|
|
398
|
-
const dttxt = dtmrk.value
|
|
399
|
-
if(ptval === dttxt)
|
|
400
|
-
return (parseFloat(dtmrk.left.replace(/[ ]?(px|\%)/g, "")) || 0)/* + (dtmrWid/2)*/ - halfPlot
|
|
401
|
-
}
|
|
402
|
-
}
|
|
403
|
-
|
|
404
|
-
for(k; k < hmrlen; k++){
|
|
405
|
-
const hm = this.dtmrksH[k]
|
|
406
|
-
const dttxt = hm.value
|
|
407
|
-
if(ptval === dttxt)
|
|
408
|
-
return Math.min(maxPtLft, ((parseFloat(hm.left.replace(/[ ]?(px|\%)/g, "")) || 0))) - halfPlot
|
|
409
|
-
}
|
|
410
|
-
return 0;
|
|
411
|
-
}
|
|
412
|
-
|
|
413
|
-
const nmrHgt = (nmrks[0]?.getBoundingClientRect().height || 17)
|
|
414
|
-
const lbot = lgbds.bottom
|
|
415
|
-
const findNBot = (num: number) => {
|
|
416
|
-
let q = 0; let k = 0
|
|
417
|
-
const ptval = num?.toLocaleString(undefined, {maximumFractionDigits: 2});
|
|
418
|
-
for(q; q < nmrlen; q++){
|
|
419
|
-
const nmrk = this.naddedtks()[q]
|
|
420
|
-
if(nmrk){
|
|
421
|
-
const dttxt = nmrk.value
|
|
422
|
-
if(ptval === dttxt)
|
|
423
|
-
return Math.floor(lbot - nmrks[q].getBoundingClientRect().bottom) + (nmrHgt/2) - halfPlot/*halfPlot is half the dot*/
|
|
424
|
-
}
|
|
425
|
-
}
|
|
426
|
-
for(k; k < hnmrlen; k++){
|
|
427
|
-
const hnmrk = hnmrks[k]
|
|
428
|
-
const dttxt = hnmrk.value
|
|
429
|
-
if((ptval === dttxt) && hnmrk.percent){
|
|
430
|
-
const totalh = Math.floor(lgbds.height*(hnmrk.percent || 0)) + (nmrHgt/2) - halfPlot
|
|
431
|
-
return Math.min(maxPtBot, Math.ceil(totalh))/*halfPlot is half the dot*/
|
|
432
|
-
}
|
|
433
|
-
}
|
|
434
|
-
return 0;
|
|
435
|
-
}
|
|
436
|
-
let p = 0;
|
|
437
|
-
let skipped = 0
|
|
438
|
-
let plots: any[] = []
|
|
439
|
-
let lgColor: string = "#00a8f3"
|
|
440
|
-
let lgUndColor: string = ""
|
|
441
|
-
const ctx = canvas.getContext("2d")
|
|
442
|
-
if(ctx){
|
|
443
|
-
let mkPlts: any[] = []
|
|
444
|
-
const whiteTxt = ["white", "#FFFFFF", "#ffffff", "rgb(255, 255, 255)", "rgb(255,255,255)"];
|
|
445
|
-
if(this.dataTableService.themeColor1 && whiteTxt.indexOf(this.dataTableService.themeColor1) < 0)
|
|
446
|
-
lgColor = this.dataTableService.themeColor1;
|
|
447
|
-
let useCol: any = lgColor
|
|
448
|
-
if(lgColor.startsWith("#"))
|
|
449
|
-
useCol = this.common.hexToRgb(lgColor)
|
|
450
|
-
else
|
|
451
|
-
useCol = this.common.getRgbParts(lgColor)
|
|
452
|
-
if(useCol && typeof useCol === "object")
|
|
453
|
-
lgUndColor = "rgba("+useCol.r+", "+useCol.g+", "+useCol.b+", 0.2)";
|
|
454
|
-
const bSym = ((sym && ["$","€","£","¥","₣","₹"].indexOf(sym) > -1) ? sym : "");
|
|
455
|
-
const aSym = ((!sym || sym && ["$","€","£","¥","₣","₹"].indexOf(sym) > -1) ? "" : sym);
|
|
456
|
-
const minFrac = bSym ? 2 : 0;//currency
|
|
457
|
-
for(p; p < dlen; p++){
|
|
458
|
-
try{
|
|
459
|
-
const dval = data[p]
|
|
460
|
-
if(!dval["dtColumn"] || (typeof dval["numColumn"] !== "number"))
|
|
461
|
-
continue;
|
|
462
|
-
const plft = Math.max(0, findDtLft(dval["dtColumn"]))
|
|
463
|
-
const pbot = Math.max(0, findNBot(dval["numColumn"]))
|
|
464
|
-
if(typeof plft !== "number" || typeof pbot !== "number")
|
|
465
|
-
continue
|
|
466
|
-
const usedval = this.common.isADateObject(dval["dtColumn"]) ? dval["dtColumn"].toLocaleDateString('en-US', dtopts)/*.replace(yrReg, "")*/ : dval["dtColumn"]
|
|
467
|
-
let pt = {
|
|
468
|
-
date: usedval,
|
|
469
|
-
number: (bSym + dval["numColumn"].toLocaleString(undefined, {maximumFractionDigits: 2, minimumFractionDigits: minFrac}) + aSym),
|
|
470
|
-
left: plft > 0 ? (plft+halfPlot) : plft,
|
|
471
|
-
bottom: (pbot+(halfPlot*2)),
|
|
472
|
-
}
|
|
473
|
-
const plen = plots.push(pt)
|
|
474
|
-
const ptBef = plots[plen-(2+skipped)]
|
|
475
|
-
if(ptBef){
|
|
476
|
-
const dx = pt.left - ptBef.left;
|
|
477
|
-
const dy = (lgbds.height-pt.bottom) - (lgbds.height-ptBef.bottom);
|
|
478
|
-
if(dx <= 20 && (p < (dlen-1))){
|
|
479
|
-
if(!nokTickVal.includes(dval["numColumn"])){
|
|
480
|
-
skipped += 1;
|
|
481
|
-
continue
|
|
482
|
-
}
|
|
483
|
-
}
|
|
484
|
-
skipped = 0
|
|
485
|
-
this.drawCanvasLineGraph(ctx, dx, dy, ptBef.left, (lgbds.height-ptBef.bottom), lgColor, lgUndColor)
|
|
486
|
-
}
|
|
487
|
-
mkPlts.push(pt)
|
|
488
|
-
}catch(e){}
|
|
489
|
-
}
|
|
490
|
-
this.plots.set(mkPlts)
|
|
491
|
-
}
|
|
492
|
-
//add plots
|
|
493
|
-
this.sizeUpBgDims()
|
|
494
|
-
this.calcCurrChangeOverTime()
|
|
495
|
-
}, 100)
|
|
496
|
-
})
|
|
497
|
-
}, 250)
|
|
498
|
-
}catch(e){}
|
|
499
|
-
}
|
|
500
|
-
}
|
|
501
|
-
|
|
502
|
-
drawCanvasLineGraph(ctx: CanvasRenderingContext2D, sideA: number, sideB: number, xb: number, yb: number, color: string, ucolor: string) {
|
|
503
|
-
// Define the vertices of the right triangle
|
|
504
|
-
// Assuming the right angle is at (x1, y2)
|
|
505
|
-
let x2 = xb + sideA;
|
|
506
|
-
let y2 = yb + sideB; // Top-right vertex
|
|
507
|
-
|
|
508
|
-
// Draw the hypotenuse
|
|
509
|
-
ctx.beginPath();
|
|
510
|
-
ctx.moveTo(xb, yb); // Start at one end of the hypotenuse
|
|
511
|
-
ctx.lineTo(x2, y2); // Draw to the other end of the hypotenuse
|
|
512
|
-
ctx.strokeStyle = color; // Make hypotenuse red for distinction
|
|
513
|
-
ctx.lineWidth = 1;
|
|
514
|
-
ctx.stroke();
|
|
515
|
-
|
|
516
|
-
// Close the path to the bottom of the canvas for filling
|
|
517
|
-
ctx.lineTo(x2, this.totalHgt); // Down to the bottom at the last x-coordinate
|
|
518
|
-
ctx.lineTo(xb, this.totalHgt); // Across to the bottom at the first x-coordinate
|
|
519
|
-
ctx.closePath(); // Connects back to the starting point (50, 100)
|
|
520
|
-
|
|
521
|
-
ctx.fillStyle = ucolor || color; // Set the fill color
|
|
522
|
-
ctx.fill(); // Fill the enclosed area
|
|
523
|
-
}
|
|
524
|
-
|
|
525
|
-
calcCurrChangeOverTime() {
|
|
526
|
-
const dttype = typeof this.useData[0].dtColumn;
|
|
527
|
-
const srtData = this.useData.filter( (a) => !!a.dtColumn ).sort( (a, b) => {
|
|
528
|
-
return (dttype !== "object" ? (parseInt(a.dtColumn.toString()) - parseInt(b.dtColumn.toString())) : ( a.dtColumn.getTime() - b.dtColumn.getTime() ))
|
|
529
|
-
})
|
|
530
|
-
const srtLen = srtData.length
|
|
531
|
-
const firstVal = srtData[0].numColumn
|
|
532
|
-
const diff = srtData[srtLen-1].numColumn - firstVal
|
|
533
|
-
this.currentDelta = (((diff > 0) ? "+ " : "") + ((diff/firstVal)*100).toLocaleString(undefined, { maximumFractionDigits: 2 }) + "%");
|
|
534
|
-
this.currentDeltaCls = diff === 0 ? "neutral" : (diff > 0 ? "success" : "error-message");
|
|
535
|
-
}
|
|
536
|
-
|
|
537
|
-
findDataMedian(data: number[], len: number): number {
|
|
538
|
-
if(len === 1)
|
|
539
|
-
return data[0]
|
|
540
|
-
if(len === 2)
|
|
541
|
-
return (data[0]+data[1])/2
|
|
542
|
-
const isOdd = len%2 !== 0
|
|
543
|
-
if(isOdd){
|
|
544
|
-
return data[Math.ceil(len/2)]
|
|
545
|
-
} else {
|
|
546
|
-
const loMed = data[len/2]
|
|
547
|
-
const hiMed = data[len/2] + 1
|
|
548
|
-
return (loMed+hiMed)/2
|
|
549
|
-
}
|
|
550
|
-
}
|
|
551
|
-
|
|
552
|
-
getColumnValueCounts(data: any[]) {
|
|
553
|
-
let i = 0
|
|
554
|
-
let count: any = {}
|
|
555
|
-
const len = data.length
|
|
556
|
-
for(i; i < len; i++){
|
|
557
|
-
const d = data[i]
|
|
558
|
-
const val = "v" + d
|
|
559
|
-
if(!count[val] || typeof count[val] === "undefined")
|
|
560
|
-
count[val] = 1
|
|
561
|
-
else
|
|
562
|
-
count[val] += 1
|
|
563
|
-
}
|
|
564
|
-
return count;
|
|
565
|
-
}
|
|
566
|
-
|
|
567
|
-
findDataMode(data: any[]): number {
|
|
568
|
-
const count = this.getColumnValueCounts(data)
|
|
569
|
-
let most = null
|
|
570
|
-
for(const prop in count){
|
|
571
|
-
if(!most)
|
|
572
|
-
most = {text: prop, amount: count[prop]}
|
|
573
|
-
if(count[prop] > most.amount)
|
|
574
|
-
most = {text: prop, amount: count[prop]}
|
|
575
|
-
}
|
|
576
|
-
if(most)
|
|
577
|
-
return parseInt(most?.text.replace("v", ""))
|
|
578
|
-
return 0;
|
|
579
|
-
}
|
|
580
|
-
|
|
581
|
-
showDotPlotInfo(dt: any, num: any): any {
|
|
582
|
-
const mrlen = this.dtmrks.length
|
|
583
|
-
const hmrlen = this.dtmrksH.length
|
|
584
|
-
let q = 0; let k = 0
|
|
585
|
-
for(q; q < mrlen; q++){
|
|
586
|
-
const dttxt = this.dtmrks[q].value
|
|
587
|
-
if(dt === dttxt)
|
|
588
|
-
return this.dtmrks[q].visible = true
|
|
589
|
-
}
|
|
590
|
-
|
|
591
|
-
for(k; k < hmrlen; k++){
|
|
592
|
-
const dttxt = this.dtmrksH[k].value
|
|
593
|
-
if(dt === dttxt)
|
|
594
|
-
return this.dtmrksH[k].visible = true
|
|
595
|
-
}
|
|
596
|
-
|
|
597
|
-
q = 0;
|
|
598
|
-
k = 0;
|
|
599
|
-
const nmrlen = this.naddedtks().length
|
|
600
|
-
const hnmrlen = this.nmrks.length
|
|
601
|
-
const ptval = num?.toLocaleString(undefined, {maximumFractionDigits: 2});
|
|
602
|
-
for(q; q < nmrlen; q++){
|
|
603
|
-
const dttxt = this.naddedtks()[q].value
|
|
604
|
-
if(ptval === dttxt)
|
|
605
|
-
this.naddedtks()[q].visible = true
|
|
606
|
-
}
|
|
607
|
-
|
|
608
|
-
for(k; k < hnmrlen; k++){
|
|
609
|
-
const dttxt = this.nmrks[k].value
|
|
610
|
-
if(ptval === dttxt)
|
|
611
|
-
return this.nmrks[k].visible = true
|
|
612
|
-
}
|
|
613
|
-
}
|
|
614
|
-
|
|
615
|
-
hideDotPlotInfo() {
|
|
616
|
-
this.dtmrks = this.dtmrks.map( n => {
|
|
617
|
-
n.visible = false
|
|
618
|
-
return n
|
|
619
|
-
})
|
|
620
|
-
this.dtmrksH = this.dtmrksH.map( n => {
|
|
621
|
-
n.visible = false
|
|
622
|
-
return n
|
|
623
|
-
})
|
|
624
|
-
this.naddedtks.set(this.naddedtks().map( n => {
|
|
625
|
-
n.visible = false
|
|
626
|
-
return n
|
|
627
|
-
}))
|
|
628
|
-
this.nmrks = this.nmrks.map( n => {
|
|
629
|
-
n.visible = false
|
|
630
|
-
return n
|
|
631
|
-
})
|
|
632
|
-
}
|
|
633
|
-
|
|
634
|
-
killPlots(msg?: string) {
|
|
635
|
-
this.lblTxt = ""
|
|
636
|
-
if(msg)
|
|
637
|
-
this.graphMsg = msg
|
|
638
|
-
this.selTDOpt = -1
|
|
639
|
-
this.plots.set([])
|
|
640
|
-
this.nmrks = []
|
|
641
|
-
this.dtmrks = []
|
|
642
|
-
this.uDates = []
|
|
643
|
-
this.dtmrksH = []
|
|
644
|
-
this.naddedtks.set([])
|
|
645
|
-
this.useData = []
|
|
646
|
-
this.currentDelta = ""
|
|
647
|
-
this.clearCanvas()
|
|
648
|
-
if(msg && /enough/g.test(msg))
|
|
649
|
-
this.sizeUpBgDims()
|
|
650
|
-
}
|
|
651
|
-
|
|
652
|
-
clearCanvas() {
|
|
653
|
-
const canvas = this.lgCanvas.nativeElement
|
|
654
|
-
if(canvas){
|
|
655
|
-
canvas.getContext("2d")?.clearRect(0, 0, canvas.width, canvas.height)
|
|
656
|
-
}
|
|
657
|
-
}
|
|
658
|
-
ngOnDestroy() {
|
|
659
|
-
this.killPlots()
|
|
660
|
-
}
|
|
661
|
-
}
|