@iamproperty/components 5.5.1-beta-1 → 5.5.1-beta-2
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/assets/css/components/actionbar.css +1 -1
- package/assets/css/components/actionbar.css.map +1 -1
- package/assets/css/components/applied-filters.css +1 -1
- package/assets/css/components/applied-filters.css.map +1 -1
- package/assets/css/components/card.css +1 -1
- package/assets/css/components/card.css.map +1 -1
- package/assets/css/components/card.global.css +1 -1
- package/assets/css/components/card.global.css.map +1 -1
- package/assets/css/components/charts.css +1 -1
- package/assets/css/components/charts.css.map +1 -1
- package/assets/css/components/fileupload.css.map +1 -1
- package/assets/css/components/nav.css.map +1 -1
- package/assets/css/components/slider.css.map +1 -1
- package/assets/css/components/tabs.css +1 -1
- package/assets/css/components/tabs.css.map +1 -1
- package/assets/css/core.min.css +1 -1
- package/assets/css/core.min.css.map +1 -1
- package/assets/css/style.min.css +1 -1
- package/assets/css/style.min.css.map +1 -1
- package/assets/js/components/accordion/accordion.component.min.js +1 -1
- package/assets/js/components/actionbar/actionbar.component.js +12 -3
- package/assets/js/components/actionbar/actionbar.component.min.js +6 -6
- package/assets/js/components/actionbar/actionbar.component.min.js.map +1 -1
- package/assets/js/components/address-lookup/address-lookup.component.min.js +1 -1
- package/assets/js/components/applied-filters/applied-filters.component.min.js +6 -6
- package/assets/js/components/applied-filters/applied-filters.component.min.js.map +1 -1
- package/assets/js/components/card/card.component.min.js +3 -3
- package/assets/js/components/chart/chart.component.js +71 -0
- package/assets/js/components/collapsible-side/collapsible-side.component.min.js +1 -1
- package/assets/js/components/fileupload/fileupload.component.js +1 -1
- package/assets/js/components/fileupload/fileupload.component.min.js +5 -5
- package/assets/js/components/fileupload/fileupload.component.min.js.map +1 -1
- package/assets/js/components/filterlist/filterlist.component.min.js +1 -1
- package/assets/js/components/header/header.component.min.js +1 -1
- package/assets/js/components/nav/nav.component.min.js +1 -1
- package/assets/js/components/notification/notification.component.min.js +1 -1
- package/assets/js/components/pagination/pagination.component.min.js +1 -1
- package/assets/js/components/search/search.component.min.js +1 -1
- package/assets/js/components/search/search.component.min.js.map +1 -1
- package/assets/js/components/table/table.component.js +2 -2
- package/assets/js/components/table/table.component.min.js +6 -6
- package/assets/js/components/table/table.component.min.js.map +1 -1
- package/assets/js/components/tabs/tabs.component.min.js +2 -2
- package/assets/js/dynamic.min.js +1 -1
- package/assets/js/dynamic.min.js.map +1 -1
- package/assets/js/modules/applied-filters.js +39 -7
- package/assets/js/modules/chart.js +613 -111
- package/assets/js/modules/fileupload.js +11 -0
- package/assets/js/modules/helpers.js +16 -0
- package/assets/js/modules/table.js +62 -11
- package/assets/js/scripts.bundle.js +31 -31
- package/assets/js/scripts.bundle.js.map +1 -1
- package/assets/js/scripts.bundle.min.js +2 -2
- package/assets/js/scripts.bundle.min.js.map +1 -1
- package/assets/sass/_elements.scss +1 -1
- package/assets/sass/_functions/variables.scss +80 -0
- package/assets/sass/_utilities.scss +1 -0
- package/assets/sass/components/actionbar.scss +16 -0
- package/assets/sass/components/applied-filters.scss +6 -48
- package/assets/sass/components/card.global.scss +4 -0
- package/assets/sass/components/card.scss +1 -1
- package/assets/sass/components/charts.scss +981 -234
- package/assets/sass/components/tabs.scss +10 -1
- package/assets/sass/elements/badge-tag.scss +82 -0
- package/assets/sass/elements/buttons.scss +13 -1
- package/assets/sass/elements/details.scss +94 -5
- package/assets/sass/elements/dialog.scss +2 -0
- package/assets/sass/elements/forms.scss +26 -22
- package/assets/sass/elements/tooltips.scss +4 -3
- package/assets/sass/foundations/root.scss +11 -0
- package/assets/sass/helpers/wider-colours.scss +11 -0
- package/assets/ts/components/actionbar/actionbar.component.ts +14 -3
- package/assets/ts/components/chart/README.md +37 -0
- package/assets/ts/components/chart/chart.component.ts +98 -0
- package/assets/ts/components/fileupload/fileupload.component.ts +1 -1
- package/assets/ts/components/table/table.component.ts +2 -2
- package/assets/ts/modules/applied-filters.ts +61 -7
- package/assets/ts/modules/chart.ts +808 -119
- package/assets/ts/modules/fileupload.ts +19 -0
- package/assets/ts/modules/helpers.ts +23 -1
- package/assets/ts/modules/table.ts +86 -12
- package/dist/components.es.js +348 -329
- package/dist/components.umd.js +58 -62
- package/dist/style.css +1 -1
- package/package.json +1 -1
- package/src/components/AppliedFilters/AppliedFilters.vue +1 -1
- package/src/components/Chart/Chart.vue +26 -96
- package/assets/sass/elements/badge.scss +0 -29
|
@@ -1,219 +1,908 @@
|
|
|
1
|
-
|
|
2
|
-
import { ucfirst, unsnake } from './helpers'
|
|
1
|
+
import { ucfirst, unsnake, numberOfDays } from './helpers'
|
|
3
2
|
|
|
4
|
-
|
|
3
|
+
// #region Functions that setup and trigger other functions
|
|
4
|
+
export const setupChart = (chartElement:any,chartOuter:any,tableElement:any) => {
|
|
5
5
|
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
6
|
+
// #region Reset the chart
|
|
7
|
+
// empty divs to re-populate
|
|
8
|
+
const chartOptions = chartOuter.querySelector('.chart__options');
|
|
9
|
+
chartOptions.innerHTML = `<span>Chart Type</span>`;
|
|
10
|
+
const chartKey = chartOuter.querySelector('.chart__key');
|
|
11
|
+
chartKey.innerHTML = '';
|
|
12
|
+
const chartGuidelines = chartOuter.querySelector('.chart__guidelines');
|
|
13
|
+
chartGuidelines.innerHTML = ``;
|
|
14
|
+
const chartYaxis = chartOuter.querySelector('.chart__yaxis');
|
|
15
|
+
chartYaxis.innerHTML = ``;
|
|
9
16
|
|
|
10
|
-
//
|
|
11
|
-
|
|
12
|
-
|
|
17
|
+
// Remove old input fields
|
|
18
|
+
Array.from(chartOuter.querySelectorAll(':scope > input[type="checkbox"],:scope > input[type="radio"]')).map((element: any) => { element.remove(); })
|
|
19
|
+
// #endregion
|
|
20
|
+
|
|
21
|
+
createTypeSwitcher(chartElement,chartKey,chartOptions);
|
|
22
|
+
|
|
23
|
+
let {xaxis,type} = getChartData(chartElement,chartOuter);
|
|
24
|
+
|
|
25
|
+
setCellData(chartElement,chartOuter,tableElement);
|
|
26
|
+
|
|
27
|
+
createChartKey(chartOuter,tableElement,chartKey);
|
|
28
|
+
createChartGuidelines(chartElement,chartOuter,chartGuidelines);
|
|
29
|
+
createChartYaxis(chartElement,chartOuter,chartYaxis);
|
|
30
|
+
|
|
31
|
+
const availableTypes = chartElement.hasAttribute('data-types') ? chartElement.getAttribute('data-types').split(',') : [type];
|
|
32
|
+
|
|
33
|
+
if(availableTypes.includes('line')){
|
|
34
|
+
createLines(chartElement,chartOuter);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
if(availableTypes.includes('pie'))
|
|
38
|
+
createPies(chartOuter);
|
|
39
|
+
|
|
40
|
+
if(xaxis){
|
|
41
|
+
createXaxis(chartElement,chartOuter,xaxis);
|
|
13
42
|
}
|
|
14
43
|
|
|
15
|
-
//
|
|
16
|
-
|
|
17
|
-
|
|
44
|
+
if(chartElement.hasAttribute('data-slope')) // Need to check attribute is there not its value
|
|
45
|
+
createSlope(chartElement,chartOuter);
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
if(chartElement.classList.contains('chart--show-totals'))
|
|
49
|
+
createKeyTotals(chartElement,chartOuter);
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
if(availableTypes.includes('bar') || availableTypes.includes('dumbbell') || availableTypes.includes('responsive'))
|
|
53
|
+
setLongestLabel(chartOuter);
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
// Event handlers
|
|
57
|
+
setEventHandlers(chartElement,chartOuter);
|
|
58
|
+
|
|
59
|
+
return true;
|
|
60
|
+
};
|
|
61
|
+
// #endregion
|
|
62
|
+
|
|
63
|
+
// #region Event handlers and observers
|
|
64
|
+
export const setEventHandlers = function(chartElement:any,chartOuter:any) {
|
|
65
|
+
const showData = chartOuter.querySelectorAll(':scope > input[type="checkbox"]');
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
let {type} = getChartData(chartElement,chartOuter);
|
|
69
|
+
|
|
70
|
+
const availableTypes = chartElement.hasAttribute('data-types') ? chartElement.getAttribute('data-types').split(',') : [type];
|
|
71
|
+
|
|
72
|
+
for (var i = 0; i < showData.length; i++) {
|
|
73
|
+
showData[i].addEventListener('change', function() {
|
|
74
|
+
|
|
75
|
+
if(availableTypes.includes('pie'))
|
|
76
|
+
createPies(chartOuter);
|
|
77
|
+
|
|
78
|
+
//setupOptionalContent(chartElement,min,max); // TODO: move this to the observer and just update the data attribute
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
/* TO DO: do i need?
|
|
82
|
+
// Update chart type
|
|
83
|
+
const chartTypes = chartElement.querySelectorAll(':scope > input[type="radio"]');
|
|
84
|
+
for (var i = 0; i < chartTypes.length; i++) {
|
|
85
|
+
chartTypes[i].addEventListener('change', function() {
|
|
86
|
+
//setupOptionalContent(chartElement,min,max); // TODO: move this to the observer and just update the data attribute
|
|
87
|
+
});
|
|
18
88
|
}
|
|
19
|
-
|
|
20
|
-
|
|
89
|
+
*/
|
|
90
|
+
};
|
|
91
|
+
|
|
92
|
+
export const setEventObservers = function(chartElement:any,chartOuter:any) {
|
|
93
|
+
|
|
94
|
+
if(chartElement.hasAttribute('data-series'))
|
|
95
|
+
return false;
|
|
96
|
+
|
|
97
|
+
let table = chartElement.querySelector('table');
|
|
98
|
+
let newTable = chartOuter.querySelector('table');
|
|
99
|
+
|
|
100
|
+
const attributesUpdated = (mutationList:any, observer:any) => {
|
|
101
|
+
|
|
102
|
+
observer.disconnect();
|
|
103
|
+
observer2.disconnect();
|
|
104
|
+
|
|
105
|
+
for (const mutation of mutationList) {
|
|
106
|
+
|
|
107
|
+
if(mutation.attributeName == 'class' || (mutation.type === 'attributes' && mutation.attributeName === 'data-total') || mutation.type === 'attributes') {
|
|
108
|
+
|
|
109
|
+
newTable.innerHTML = table.innerHTML;
|
|
110
|
+
setupChart(chartElement,chartOuter,newTable);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
observer.observe(table, { characterData: true, subtree: true });
|
|
115
|
+
observer2.observe(chartElement, { attributes: true });
|
|
116
|
+
};
|
|
117
|
+
|
|
118
|
+
const tableUpdated = (mutationList:any, observer:any) => {
|
|
119
|
+
|
|
120
|
+
observer.disconnect();
|
|
121
|
+
observer2.disconnect();
|
|
122
|
+
|
|
123
|
+
for (const mutation of mutationList) {
|
|
124
|
+
|
|
125
|
+
if(mutation.type == "characterData" || (mutation.type == "childList" && mutation.addedNodes.length)){
|
|
126
|
+
|
|
127
|
+
newTable.innerHTML = table.innerHTML;
|
|
128
|
+
setupChart(chartElement,chartOuter,newTable);
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
observer.observe(table, { characterData: true, subtree: true });
|
|
133
|
+
observer2.observe(chartElement, { attributes: true });
|
|
134
|
+
};
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
let observer = new MutationObserver(tableUpdated);
|
|
138
|
+
let observer2 = new MutationObserver(attributesUpdated);
|
|
139
|
+
|
|
140
|
+
observer.observe(table, { characterData: true, subtree: true });
|
|
141
|
+
observer2.observe(chartElement, { attributes: true });
|
|
142
|
+
|
|
143
|
+
return true;
|
|
144
|
+
};
|
|
145
|
+
// #endregion
|
|
146
|
+
|
|
147
|
+
// #region GET functions
|
|
148
|
+
export const getChartData = function(chartElement:any,chartOuter:any){
|
|
149
|
+
|
|
150
|
+
|
|
151
|
+
let table = chartOuter.querySelector('.chart__wrapper table');
|
|
152
|
+
|
|
153
|
+
let min:any = chartElement.hasAttribute('data-min') ? chartElement.getAttribute('data-min') : 0;
|
|
154
|
+
let max:any = chartElement.hasAttribute('data-max') ? chartElement.getAttribute('data-max') : getLargestValue(table);
|
|
155
|
+
let type:string = chartElement.hasAttribute('data-type') ? chartElement.getAttribute('data-type') : 'column';
|
|
156
|
+
let yaxis:any = chartElement.hasAttribute('data-yaxis') ? chartElement.getAttribute('data-yaxis').split(',') : [];
|
|
157
|
+
let guidelines:any = chartElement.hasAttribute('data-guidelines') ? chartElement.getAttribute('data-guidelines').split(',') : [];
|
|
158
|
+
let targets:any = chartElement.hasAttribute('data-targets') ? JSON.parse(chartElement.getAttribute('data-targets')) : null;
|
|
159
|
+
let events:any = chartElement.hasAttribute('data-events') ? JSON.parse(chartElement.getAttribute('data-events')) : null;
|
|
160
|
+
let xaxis:any = chartElement.hasAttribute('data-xaxis') ? chartElement.getAttribute('data-xaxis').split(',') : null;
|
|
161
|
+
let increment = chartElement.hasAttribute('data-increment') ? chartElement.getAttribute('data-increment'): null;
|
|
162
|
+
|
|
163
|
+
let start:any = chartElement.hasAttribute('data-start') ? chartElement.getAttribute('data-start') : 0;
|
|
164
|
+
let end:any = chartElement.hasAttribute('data-end') ? chartElement.getAttribute('data-end') : getLargestValue(table); // TODO - get largest value from the data-xaxis
|
|
165
|
+
|
|
166
|
+
|
|
167
|
+
let slope:any = chartElement.hasAttribute('data-slope') ? chartElement.getAttribute('data-slope') : null;
|
|
168
|
+
let yInt:any = chartElement.hasAttribute('data-yint') ? chartElement.getAttribute('data-yint') : null;
|
|
169
|
+
|
|
170
|
+
return {min,max,type,yaxis,targets,events,xaxis,increment,start,end,slope,yInt,guidelines};
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
function getLargestValue(table:any){
|
|
174
|
+
|
|
175
|
+
let values = Array.from(table.querySelectorAll('tbody td:not(:first-child)')).map((element: any) => {
|
|
176
|
+
|
|
177
|
+
let currentValue:string|number = String(element.textContent);
|
|
178
|
+
currentValue = currentValue.replace('£','');
|
|
179
|
+
currentValue = currentValue.replace('%','');
|
|
180
|
+
currentValue = currentValue.replace(',','');
|
|
181
|
+
currentValue = Number.parseFloat(currentValue);
|
|
182
|
+
|
|
183
|
+
return currentValue;
|
|
184
|
+
})
|
|
185
|
+
|
|
186
|
+
let largetValue:number = Math.max(...values);
|
|
187
|
+
|
|
188
|
+
// TO DO round to the nearest 10, 100, 1000 and so on
|
|
189
|
+
return Math.ceil(largetValue);
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
const getValues = function(value:number,min:any,max:any,start?:number){
|
|
193
|
+
|
|
194
|
+
let cleanValue:string|number = String(value);
|
|
195
|
+
cleanValue = cleanValue.replace('£','');
|
|
196
|
+
cleanValue = cleanValue.replace('%','');
|
|
197
|
+
cleanValue = cleanValue.replace(',','');
|
|
198
|
+
cleanValue = Number.parseFloat(cleanValue);
|
|
199
|
+
|
|
200
|
+
let percent = ((cleanValue - min)/(max - min)) * 100;
|
|
201
|
+
let axis = percent;
|
|
202
|
+
let bottom = 0;
|
|
203
|
+
|
|
204
|
+
if (start && start != 0){
|
|
205
|
+
bottom = ((start - min)/(max - min)) * 100;
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
// If the value is negative the position below the 0 line
|
|
209
|
+
if(min < 0){
|
|
210
|
+
bottom = Math.abs(((min)/(max - min))*100);
|
|
211
|
+
|
|
212
|
+
if(cleanValue < 0){
|
|
213
|
+
percent = bottom - percent;
|
|
214
|
+
bottom = bottom - percent;
|
|
215
|
+
axis = bottom;
|
|
216
|
+
}
|
|
217
|
+
else {
|
|
218
|
+
percent = percent - bottom;
|
|
219
|
+
axis = percent + bottom;
|
|
220
|
+
}
|
|
21
221
|
}
|
|
22
222
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
223
|
+
return { percent, axis, bottom};
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
function getCoordinatesForPercent(percent:number, pieCount:number) {
|
|
227
|
+
|
|
228
|
+
// This moves the start point to the top middle point like a clock
|
|
229
|
+
if(pieCount > 1)
|
|
230
|
+
percent = percent - 0.25;
|
|
231
|
+
|
|
232
|
+
const x = Math.cos(2 * Math.PI * percent);
|
|
233
|
+
const y = Math.sin(2 * Math.PI * percent);
|
|
234
|
+
return [x*100, y*100];
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
// #endregion
|
|
238
|
+
|
|
239
|
+
// #region SET functions - set data attributes and classes
|
|
240
|
+
export const setCellData = function(chartElement:any,chartOuter:any,table:any){
|
|
241
|
+
|
|
242
|
+
let {min, max} = getChartData(chartElement,chartOuter);
|
|
243
|
+
|
|
244
|
+
let chartType = chartElement.getAttribute('data-type');
|
|
245
|
+
let increment = chartElement.getAttribute('data-increment');
|
|
246
|
+
let incrementStart = chartElement.getAttribute('data-start');
|
|
247
|
+
let incrementEnd = chartElement.getAttribute('data-end');
|
|
248
|
+
let startDay = min;
|
|
249
|
+
|
|
250
|
+
// Change how gant charts are configured as this just seems bizarre now
|
|
251
|
+
if(increment == "days"){
|
|
252
|
+
|
|
253
|
+
max = numberOfDays(min,max);
|
|
254
|
+
min = 0;
|
|
255
|
+
|
|
256
|
+
chartElement.querySelector('tbody').setAttribute('style',`--single-day:${((1/max)*100)}%;`);
|
|
257
|
+
}
|
|
26
258
|
|
|
27
|
-
// Create pies
|
|
28
|
-
if(type == "pie")
|
|
29
|
-
createPies(chartElement);
|
|
30
259
|
|
|
31
|
-
|
|
32
|
-
Array.from(chartElement.querySelectorAll('tbody tr')).forEach((tr, index) => {
|
|
260
|
+
Array.from(table.querySelectorAll('tbody tr')).forEach((tr:any, index) => {
|
|
33
261
|
|
|
34
262
|
let group = tr.querySelector('td:first-child, th:first-child') ? tr.querySelector('td:first-child, th:first-child').innerHTML : '';
|
|
263
|
+
let coverageStart:number = 100;
|
|
264
|
+
let coverageEnd:number = 0;
|
|
265
|
+
let cumulativeComparison:number = 0;
|
|
266
|
+
// For waffle charts
|
|
267
|
+
let previousAfter:number = 0;
|
|
268
|
+
let rowPosition:number = 0;
|
|
269
|
+
let totalPercent:number = 0;
|
|
270
|
+
|
|
271
|
+
// Set the data numeric value if not set
|
|
272
|
+
Array.from(tr.querySelectorAll('td:not([data-numeric]):not(:first-child)')).forEach((td:any) => {
|
|
273
|
+
|
|
274
|
+
let value = parseFloat(td.textContent.replace('£','').replace('%','').replace(',',''));
|
|
275
|
+
let start = 0;
|
|
276
|
+
if(increment == "days"){
|
|
277
|
+
let dates = td.textContent.split(' - ');
|
|
278
|
+
if(dates[1]){
|
|
279
|
+
|
|
280
|
+
value = numberOfDays(dates[0],dates[1]);
|
|
281
|
+
start = numberOfDays(startDay,dates[0]) - 1;
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
td.setAttribute('data-numeric',value);
|
|
286
|
+
td.setAttribute('data-start',start);
|
|
287
|
+
});
|
|
35
288
|
|
|
36
|
-
|
|
289
|
+
// Set the data label value if not set
|
|
290
|
+
Array.from(tr.querySelectorAll('td:not([data-label])')).forEach((td:any, index) => {
|
|
37
291
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
292
|
+
td.setAttribute('data-label',table.querySelectorAll('thead th')[index].textContent);
|
|
293
|
+
});
|
|
294
|
+
|
|
295
|
+
if(tr.querySelector('[data-label="Total"]')){
|
|
296
|
+
tr.setAttribute('data-total',tr.querySelector('[data-label="Total"][data-numeric]').getAttribute('data-numeric'));
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
if(tr.querySelector('[data-label="Min"]')){
|
|
300
|
+
tr.setAttribute('data-min',tr.querySelector('[data-label="Min"][data-numeric]').getAttribute('data-numeric'));
|
|
301
|
+
}
|
|
302
|
+
if(tr.querySelector('[data-label="Max"]')){
|
|
303
|
+
tr.setAttribute('data-max',tr.querySelector('[data-label="Max"][data-numeric]').getAttribute('data-numeric'));
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
if((chartType == "proportional" || chartType == "waffle") && !tr.hasAttribute('data-total')){
|
|
307
|
+
|
|
308
|
+
let total = 0;
|
|
309
|
+
|
|
310
|
+
Array.from(tr.querySelectorAll('td[data-numeric]:not(:first-child)')).forEach((td:any) => {
|
|
311
|
+
|
|
312
|
+
let display = getComputedStyle(td).display;
|
|
313
|
+
if(display == 'none')
|
|
314
|
+
return;
|
|
315
|
+
|
|
316
|
+
total += Number.parseFloat(td.getAttribute('data-numeric'));
|
|
317
|
+
});
|
|
318
|
+
|
|
319
|
+
tr.setAttribute('data-total',total);
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
let rowMin = tr.hasAttribute('data-min') ? tr.getAttribute('data-min') : min;
|
|
323
|
+
let rowMax = tr.hasAttribute('data-max') ? tr.getAttribute('data-max') : max;
|
|
324
|
+
|
|
325
|
+
// Add a useful index css var for the use of animatons.
|
|
326
|
+
tr.setAttribute('style',`--row-index:${index+1};`);
|
|
327
|
+
|
|
328
|
+
if(rowMin < 0){
|
|
329
|
+
let minBottom = Math.abs(((rowMin)/(rowMax - rowMin))*100);
|
|
330
|
+
chartElement.setAttribute('style',`--min-bottom: ${minBottom}%;`);
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
// Add css vars to cells
|
|
334
|
+
Array.from(tr.querySelectorAll('td[data-numeric]:not([data-label="Min"]):not([data-label="Max"]):not(:first-child)')).forEach((td:any) => {
|
|
335
|
+
|
|
336
|
+
let display = getComputedStyle(td).display;
|
|
337
|
+
if(display == 'none')
|
|
338
|
+
return;
|
|
339
|
+
|
|
41
340
|
const label = td.getAttribute('data-label');
|
|
42
|
-
|
|
341
|
+
const content = td.innerHTML;
|
|
342
|
+
const value = Number.parseFloat(td.getAttribute('data-numeric'));
|
|
343
|
+
const start = Number.parseFloat(td.getAttribute('data-start'));
|
|
344
|
+
|
|
345
|
+
if(!td.querySelector('span[data-group]'))
|
|
346
|
+
td.innerHTML = `<span data-group="${group}" data-label="${label}">${content}</span>`;
|
|
347
|
+
|
|
348
|
+
if(!td.hasAttribute('style')){
|
|
349
|
+
|
|
350
|
+
let { percent, bottom, axis } = getValues(value,rowMin,rowMax,start);
|
|
351
|
+
|
|
352
|
+
td.setAttribute('data-percent',percent)
|
|
353
|
+
td.setAttribute("style",`--bottom:${bottom}%;--percent:${percent}%;--axis:${axis}%;`);
|
|
354
|
+
|
|
355
|
+
if(tr.hasAttribute('data-total')){
|
|
356
|
+
let rowTotal = tr.getAttribute('data-total');
|
|
357
|
+
let comparison = ((value - rowMin)/(rowTotal)) * 100;
|
|
358
|
+
cumulativeComparison += comparison;
|
|
359
|
+
td.setAttribute('data-comparison',comparison);
|
|
360
|
+
td.style.setProperty('--cumulative-comparision',`${cumulativeComparison}%`);
|
|
361
|
+
td.style.setProperty('--comparison',`${comparison}%`);
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
if(chartElement.classList.contains("chart--value-order")){
|
|
365
|
+
let order:number = (10000 - Math.round(percent * 100));
|
|
366
|
+
td.style.setProperty('--order',`${order}%`);
|
|
367
|
+
}
|
|
43
368
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
369
|
+
if(chartType == "dumbbell"){
|
|
370
|
+
if(percent < coverageStart){
|
|
371
|
+
tr.style.setProperty('--coverage-start',`${percent}%`);
|
|
372
|
+
coverageStart = percent;
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
if(percent > coverageEnd){
|
|
376
|
+
tr.style.setProperty('--coverage-end',`${percent}%`);
|
|
377
|
+
coverageEnd = percent;
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
if(chartType == "waffle") {
|
|
382
|
+
|
|
383
|
+
let actualPercent = Math.round(td.getAttribute('data-comparison'));
|
|
384
|
+
|
|
385
|
+
// Prevent the chart from spilling out of the top
|
|
386
|
+
totalPercent += actualPercent;
|
|
387
|
+
if(totalPercent > 100)
|
|
388
|
+
actualPercent = actualPercent - (totalPercent - 100);
|
|
389
|
+
|
|
390
|
+
let percentMinusAfter = previousAfter != 0 ? actualPercent - (10 - previousAfter) : actualPercent;
|
|
391
|
+
let rowHeight = percentMinusAfter < 10 ? 10 : Math.floor(percentMinusAfter/10)*10;
|
|
392
|
+
let rowWidth = percentMinusAfter < 10 ? percentMinusAfter*10 : 100;
|
|
393
|
+
let maxWidth = actualPercent*10;
|
|
394
|
+
|
|
395
|
+
|
|
396
|
+
|
|
397
|
+
td.style.setProperty('--rowPosition',`${rowPosition}%`);
|
|
398
|
+
td.style.setProperty('--rowHeight',`${rowHeight}%`);
|
|
399
|
+
td.style.setProperty('--rowWidth',`${rowWidth}%`);
|
|
400
|
+
td.style.setProperty('--maxWidth',`${maxWidth}%`);
|
|
401
|
+
|
|
402
|
+
// Create the psuedo element variables for the the block that sticks out BELOW the main row
|
|
403
|
+
let beforeWidth = 0;
|
|
404
|
+
if(previousAfter != 0){
|
|
405
|
+
beforeWidth = 100 - (previousAfter*10);
|
|
406
|
+
|
|
407
|
+
td.style.setProperty('--beforeWidth',`${beforeWidth}%`);
|
|
408
|
+
td.style.setProperty('--beforeHeight',`${10/rowHeight * 100}%`);
|
|
409
|
+
td.style.setProperty('--beforeLeft',`${previousAfter*10}%`);
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
// Create the psuedo element variables for the the block that sticks out ABOVE the main row
|
|
413
|
+
let afterWidth = Math.round(percentMinusAfter - rowHeight)*10;
|
|
414
|
+
let afterHeight = 10/(rowHeight) * 100;
|
|
415
|
+
|
|
416
|
+
td.style.setProperty('--afterWidth',`${afterWidth}%`);
|
|
417
|
+
td.style.setProperty('--afterHeight',`${afterHeight}%`);
|
|
418
|
+
|
|
419
|
+
// If the row width plus the previous after is under 10 it needs to be added to the new previousAfter variable
|
|
420
|
+
if(previousAfter + beforeWidth/10 + rowWidth/10 < 10 )
|
|
421
|
+
previousAfter += beforeWidth/10 + (rowWidth/10);
|
|
422
|
+
else if (percentMinusAfter < 10)
|
|
423
|
+
previousAfter = percentMinusAfter;
|
|
424
|
+
else
|
|
425
|
+
previousAfter = afterWidth/10;
|
|
426
|
+
|
|
427
|
+
// Add to the row position so that the new row is shoved up if needed
|
|
428
|
+
rowPosition += (rowWidth > 0 ? rowHeight : 0) + (afterWidth > 0 ? 10 : 0);
|
|
49
429
|
}
|
|
50
430
|
}
|
|
51
|
-
td.setAttribute("style",`--bottom:${bottom}%;--percent:${percent}%;`);
|
|
52
431
|
|
|
432
|
+
// totals
|
|
433
|
+
if(chartElement.classList.contains('chart--show-totals')){
|
|
53
434
|
|
|
54
|
-
|
|
435
|
+
let chartTotal = chartElement.getAttribute('data-total') ? Number.parseFloat(chartElement.getAttribute('data-total')) : 0;
|
|
436
|
+
let keyTotal = chartElement.querySelector(`.key[data-label="${label}"]`) && chartElement.querySelector(`.key[data-label="${label}"]`).getAttribute('data-total') ? Number.parseFloat(chartElement.querySelector(`.key[data-label="${label}"]`).getAttribute('data-total')) : 0;
|
|
437
|
+
|
|
438
|
+
if(chartElement.querySelector(`.key[data-label="${label}"]`))
|
|
439
|
+
chartElement.querySelector(`.key[data-label="${label}"]`).setAttribute('data-total',keyTotal+value);
|
|
440
|
+
|
|
441
|
+
chartElement.setAttribute('data-total',chartTotal+value);
|
|
442
|
+
}
|
|
55
443
|
});
|
|
444
|
+
|
|
445
|
+
// Values for incremental charts i.e. histograms...
|
|
446
|
+
if(increment && incrementStart && incrementEnd){
|
|
447
|
+
let firstCellValue = parseFloat(tr.querySelector('td:first-child').textContent.replace('£','').replace('%','').replace(',',''));
|
|
448
|
+
let position = ((firstCellValue - incrementStart)/(incrementEnd - incrementStart)) * 100;
|
|
449
|
+
tr.setAttribute('style',`--position:${position}%;`);
|
|
450
|
+
}
|
|
56
451
|
});
|
|
57
452
|
}
|
|
58
453
|
|
|
59
|
-
export const
|
|
454
|
+
export const setLongestLabel = function(chartOuter:any){
|
|
455
|
+
let chartWrapper = chartOuter.querySelector('.chart__wrapper');
|
|
456
|
+
let table = chartOuter.querySelector('.chart table');
|
|
457
|
+
// set the longest label attr so that the bar chart knows what margin to set on the left
|
|
458
|
+
let longestLabel = '';
|
|
459
|
+
Array.from(table.querySelectorAll('tbody tr td:first-child')).forEach((td: any) => {
|
|
460
|
+
if(typeof td.textContent != "undefined" && td.textContent.length > longestLabel.length)
|
|
461
|
+
longestLabel = td.textContent;
|
|
462
|
+
});
|
|
463
|
+
chartWrapper.setAttribute('data-longest-label',longestLabel);
|
|
464
|
+
|
|
465
|
+
// set the longest data set attr so that the bar chart knows what margin to set on the left
|
|
466
|
+
let longestSet = '';
|
|
467
|
+
Array.from(table.querySelectorAll('thead tr th')).forEach((td: any) => {
|
|
468
|
+
if(td.textContent.length > longestSet.length)
|
|
469
|
+
longestSet = td.textContent;
|
|
470
|
+
});
|
|
471
|
+
chartWrapper.setAttribute('data-set-label',longestSet);
|
|
472
|
+
};
|
|
473
|
+
// #endregion
|
|
474
|
+
|
|
475
|
+
// #region CREATE function
|
|
476
|
+
export const createTypeSwitcher = function(chartElement:any,chartKey:any,chartOptions:any){
|
|
477
|
+
|
|
478
|
+
const chartID = `chart-${Date.now()+(Math.floor(Math.random() * 100) + 1)}`;
|
|
479
|
+
const availableTypes = chartElement.hasAttribute('data-types') ? chartElement.getAttribute('data-types').split(',') : [];
|
|
480
|
+
|
|
481
|
+
if(!chartElement.hasAttribute('data-types') && chartElement.hasAttribute('data-type'))
|
|
482
|
+
chartKey.insertAdjacentHTML('afterend', `<input type="radio" name="chart-type" value="${chartElement.getAttribute('data-type')}" checked="">`);
|
|
483
|
+
else if (chartElement.hasAttribute('data-types')){
|
|
484
|
+
|
|
485
|
+
let chartType = chartElement.hasAttribute('data-type') ? chartElement.getAttribute('data-type') : 'column';
|
|
486
|
+
chartOptions.insertAdjacentHTML('beforebegin', availableTypes.map((type:any) => `<input type="radio" name="chart-type" value="${type}" id="${chartID}-${type}" ${chartType == type ? 'checked=""' : '' }>`).join(''));
|
|
487
|
+
chartOptions.insertAdjacentHTML('beforeend', availableTypes.map((type:any) => `<label for="${chartID}-${type}">${type}</label>` ).join(''));
|
|
488
|
+
}
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
export const createChartKey = function(chartOuter:any,tableElement:any,chartKey:any){
|
|
492
|
+
|
|
493
|
+
const chartID = `chart-${Date.now()+(Math.floor(Math.random() * 100) + 1)}`;
|
|
494
|
+
//const chartOuter = chartElement.querySelector('.chart__outer');
|
|
495
|
+
|
|
496
|
+
let previousInput:any;
|
|
497
|
+
|
|
60
498
|
|
|
61
|
-
let
|
|
499
|
+
let headings = Array.from(tableElement.querySelectorAll('thead th'));
|
|
62
500
|
|
|
63
|
-
|
|
501
|
+
headings.forEach((arrayElement:any , index) => {
|
|
502
|
+
|
|
503
|
+
if(index != 0){
|
|
504
|
+
|
|
505
|
+
previousInput = createChartKeyItem(chartID,index,arrayElement.textContent,chartKey,chartOuter,previousInput);
|
|
506
|
+
}
|
|
64
507
|
|
|
65
|
-
|
|
508
|
+
if(index == 50){
|
|
509
|
+
headings.length = index + 1;
|
|
510
|
+
}
|
|
66
511
|
});
|
|
512
|
+
|
|
513
|
+
return true;
|
|
67
514
|
}
|
|
68
515
|
|
|
69
|
-
|
|
516
|
+
function createChartKeyItem(chartID:string,index:number,text:Array<string>,chartKey:any,chartOuter:any,previousInput:any){
|
|
517
|
+
let input = document.createElement('input');
|
|
518
|
+
input.setAttribute('name',`${chartID}-dataset-${index}`);
|
|
519
|
+
input.setAttribute('id',`${chartID}-dataset-${index}`);
|
|
520
|
+
input.setAttribute('checked',`checked`);
|
|
521
|
+
input.setAttribute('type',`checkbox`);
|
|
522
|
+
|
|
523
|
+
if(index == 1)
|
|
524
|
+
chartOuter.prepend(input);
|
|
525
|
+
else
|
|
526
|
+
chartOuter.insertBefore(input,previousInput.nextSibling);
|
|
70
527
|
|
|
71
|
-
|
|
72
|
-
const max = chartElement.getAttribute('data-max');
|
|
73
|
-
const min = chartElement.getAttribute('data-min');
|
|
528
|
+
previousInput = input;
|
|
74
529
|
|
|
75
|
-
|
|
76
|
-
|
|
530
|
+
let label = document.createElement('label');
|
|
531
|
+
label.setAttribute('class',`key btn btn-action`);
|
|
532
|
+
label.setAttribute('for',`${chartID}-dataset-${index}`);
|
|
533
|
+
label.setAttribute('data-label',`${text}`);
|
|
534
|
+
label.innerHTML = `${text}`;
|
|
535
|
+
chartKey.append(label);
|
|
536
|
+
|
|
537
|
+
return previousInput;
|
|
77
538
|
}
|
|
78
539
|
|
|
79
|
-
export const
|
|
540
|
+
export const createChartGuidelines = function(chartElement:any,chartOuter:any,chartGuidelines:any){
|
|
541
|
+
|
|
542
|
+
let {min, max, yaxis, increment, guidelines} = getChartData(chartElement,chartOuter);
|
|
543
|
+
|
|
544
|
+
if(guidelines.length)
|
|
545
|
+
yaxis = guidelines;
|
|
546
|
+
|
|
547
|
+
|
|
548
|
+
let startDay = min;
|
|
549
|
+
if(increment == "days"){
|
|
550
|
+
|
|
551
|
+
max = numberOfDays(min,max);
|
|
552
|
+
min = 0;
|
|
553
|
+
}
|
|
554
|
+
|
|
555
|
+
if(!chartGuidelines){
|
|
556
|
+
chartGuidelines = document.createElement('div');
|
|
557
|
+
chartGuidelines.setAttribute('class','chart__guidelines');
|
|
558
|
+
}
|
|
559
|
+
|
|
560
|
+
chartGuidelines.innerHTML = '';
|
|
561
|
+
for (var i = 0; i < yaxis.length; i++) {
|
|
80
562
|
|
|
81
|
-
|
|
82
|
-
const max = chartElement.getAttribute('data-max');
|
|
83
|
-
const min = chartElement.getAttribute('data-min');
|
|
563
|
+
let value = parseFloat(yaxis[i].replace('£','').replace('%','').replace(',',''));
|
|
84
564
|
|
|
85
|
-
|
|
86
|
-
|
|
565
|
+
|
|
566
|
+
if(increment == "days"){
|
|
567
|
+
|
|
568
|
+
value = numberOfDays(startDay,yaxis[i]) - 1;
|
|
569
|
+
}
|
|
570
|
+
|
|
571
|
+
let { axis } = getValues(value,min,max);
|
|
572
|
+
chartGuidelines.innerHTML += `<div class="guideline" style="--percent:${axis}%;"><span>${yaxis[i]}</span></div>`;
|
|
573
|
+
}
|
|
87
574
|
}
|
|
88
575
|
|
|
89
|
-
function
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
576
|
+
export const createChartYaxis = function(chartElement:any,chartOuter:any,chartYaxis:any){
|
|
577
|
+
|
|
578
|
+
let {min, max, yaxis, increment} = getChartData(chartElement,chartOuter);
|
|
579
|
+
|
|
580
|
+
let startDay = min;
|
|
581
|
+
|
|
582
|
+
if(increment == "days"){
|
|
583
|
+
|
|
584
|
+
max = numberOfDays(min,max);
|
|
585
|
+
min = 0;
|
|
586
|
+
}
|
|
587
|
+
|
|
588
|
+
if(!chartYaxis){
|
|
589
|
+
chartYaxis = document.createElement('div');
|
|
590
|
+
chartYaxis.setAttribute('class','chart__yaxis');
|
|
591
|
+
}
|
|
592
|
+
|
|
593
|
+
chartYaxis.innerHTML = '';
|
|
594
|
+
for (var i = 0; i < yaxis.length; i++) {
|
|
595
|
+
|
|
596
|
+
let value = parseFloat(yaxis[i].replace('£','').replace('%',''));
|
|
597
|
+
|
|
598
|
+
if(increment == "days"){
|
|
599
|
+
|
|
600
|
+
value = numberOfDays(startDay,yaxis[i]);
|
|
601
|
+
|
|
602
|
+
}
|
|
603
|
+
|
|
604
|
+
let { axis } = getValues(value,min,max);
|
|
605
|
+
chartYaxis.innerHTML += `<div class="axis__point" style="--percent:${axis}%;"><span>${yaxis[i]}</span></div>`;
|
|
606
|
+
}
|
|
607
|
+
}
|
|
608
|
+
|
|
609
|
+
export const createXaxis = function(chartElement:any,chartOuter:any,xaxis:any){
|
|
610
|
+
|
|
611
|
+
const chart = chartOuter.querySelector('.chart');
|
|
612
|
+
let chartXaxis = chartOuter.querySelector('.chart__xaxis');
|
|
613
|
+
|
|
614
|
+
let {increment,start,end} = getChartData(chartElement,chartOuter);
|
|
615
|
+
|
|
616
|
+
if(!chartXaxis){
|
|
617
|
+
chartXaxis = document.createElement('div');
|
|
618
|
+
chartXaxis.setAttribute('class','chart__xaxis');
|
|
619
|
+
}
|
|
620
|
+
if(increment && start && end){
|
|
621
|
+
chartXaxis.innerHTML = '';
|
|
622
|
+
for (var i = 0; i < xaxis.length; i++) {
|
|
623
|
+
|
|
624
|
+
let value = parseFloat(xaxis[i].replace('£','').replace('%',''));
|
|
625
|
+
let position = ((value - start)/(end - start)) * 100;
|
|
626
|
+
|
|
627
|
+
chartXaxis.innerHTML += `<div class="axis__point" style="--percent:${position}%;"><span>${xaxis[i]}</span></div>`;
|
|
628
|
+
}
|
|
629
|
+
}
|
|
630
|
+
chart.prepend(chartXaxis);
|
|
631
|
+
}
|
|
632
|
+
|
|
633
|
+
export const createLines = function(chartElement:any,chartOuter:any){
|
|
634
|
+
|
|
635
|
+
let {min, max} = getChartData(chartElement,chartOuter);
|
|
636
|
+
|
|
637
|
+
let chartType = chartElement.getAttribute('data-type');
|
|
638
|
+
let returnString = '';
|
|
639
|
+
//let chartWrapper = chartOuter.querySelector('.chart__wrapper');
|
|
640
|
+
let linesWrapper = chartOuter.querySelector('.chart__lines');
|
|
641
|
+
|
|
642
|
+
let items = Array.from(chartOuter.querySelectorAll('tbody tr'));
|
|
643
|
+
let lines = Array();
|
|
644
|
+
let linesCount = chartOuter.querySelectorAll('thead th:not(:first-child)').length;
|
|
645
|
+
let commands = Array();
|
|
646
|
+
let animatelines = Array();
|
|
647
|
+
let itemCount = items.length <= 1000 ? items.length : 1000;
|
|
648
|
+
let spacer = 200/(itemCount - 1);
|
|
649
|
+
let spacerIndent = 0;
|
|
650
|
+
|
|
651
|
+
if(chartType == "combo"){
|
|
652
|
+
|
|
653
|
+
spacer = 200/(itemCount);
|
|
654
|
+
spacerIndent = spacer/2;
|
|
655
|
+
}
|
|
656
|
+
|
|
657
|
+
// Creates the lines array from the fields array
|
|
658
|
+
for(let i = 0; i < linesCount; i++){
|
|
659
|
+
|
|
660
|
+
lines[i] = '';
|
|
661
|
+
animatelines[i] = '';
|
|
662
|
+
commands[i] = 'M';
|
|
663
|
+
}
|
|
664
|
+
|
|
665
|
+
// populate the lines array from the items array
|
|
666
|
+
let counter = 0;
|
|
667
|
+
|
|
668
|
+
Array.from(chartOuter.querySelectorAll('tbody tr')).forEach((item:any) => {
|
|
669
|
+
|
|
670
|
+
const display = getComputedStyle(item).display;
|
|
671
|
+
|
|
672
|
+
if(display != "none"){
|
|
673
|
+
|
|
674
|
+
Array.from(item.querySelectorAll('td:not(:first-child)')).forEach((cell:any, subindex) => {
|
|
675
|
+
|
|
676
|
+
if(!cell.classList.contains('chart__bar')){
|
|
677
|
+
|
|
678
|
+
let value = cell.getAttribute('data-numeric');
|
|
679
|
+
|
|
680
|
+
let { axis } = getValues(value,min,max);
|
|
681
|
+
|
|
682
|
+
if(!Number.isNaN(axis)){
|
|
683
|
+
lines[subindex] += `${commands[subindex]} ${(spacerIndent) + (spacer * counter)} ${100-axis} `;
|
|
684
|
+
animatelines[subindex] += `${commands[subindex]} ${spacer * counter} 100 `;
|
|
685
|
+
commands[subindex] = 'L';
|
|
686
|
+
}
|
|
687
|
+
else {
|
|
688
|
+
commands[subindex] = 'M';
|
|
689
|
+
}
|
|
690
|
+
}
|
|
691
|
+
});
|
|
692
|
+
|
|
693
|
+
counter++;
|
|
694
|
+
}
|
|
695
|
+
|
|
696
|
+
});
|
|
697
|
+
|
|
698
|
+
lines.forEach((line, index) => {
|
|
699
|
+
|
|
700
|
+
returnString += `
|
|
701
|
+
<svg viewBox="0 0 200 100" class="line" preserveAspectRatio="none">
|
|
702
|
+
<path fill="none" d="${line}" style="--path: path('${animatelines[index]}');"></path>
|
|
703
|
+
</svg>`
|
|
704
|
+
});
|
|
705
|
+
|
|
706
|
+
linesWrapper.innerHTML = returnString;
|
|
93
707
|
}
|
|
94
708
|
|
|
95
|
-
export const createPies = function(
|
|
709
|
+
export const createPies = function(chartOuter:any){
|
|
96
710
|
|
|
97
711
|
let returnString = '';
|
|
98
|
-
let
|
|
712
|
+
let chartInner = chartOuter.querySelector('.chart');
|
|
713
|
+
let pieWrapper = chartOuter.querySelector('.pies');
|
|
714
|
+
|
|
715
|
+
if(!pieWrapper){
|
|
716
|
+
pieWrapper = document.createElement("div");
|
|
717
|
+
pieWrapper.setAttribute('class','pies');
|
|
718
|
+
chartInner.append(pieWrapper);
|
|
719
|
+
}
|
|
99
720
|
|
|
100
|
-
Array.from(
|
|
721
|
+
Array.from(chartInner.querySelectorAll('tbody tr')).forEach((item:any, index) => {
|
|
101
722
|
|
|
102
723
|
let paths = '';
|
|
103
724
|
let tooltips = '';
|
|
104
|
-
|
|
105
725
|
let cumulativePercent = 0;
|
|
106
|
-
|
|
107
726
|
let total = 0;
|
|
108
|
-
|
|
109
727
|
let titleKey = item.querySelectorAll('td')[0]
|
|
110
728
|
let title = titleKey.innerHTML;
|
|
729
|
+
let pieCount = 0;
|
|
730
|
+
|
|
731
|
+
// Work out the total amount
|
|
732
|
+
Array.from(item.querySelectorAll('td')).forEach((td:any, subindex) => {
|
|
111
733
|
|
|
112
|
-
|
|
734
|
+
const display = getComputedStyle(td).display;
|
|
113
735
|
|
|
114
|
-
if(subindex != 0){
|
|
736
|
+
if(subindex != 0 && display != 'none'){
|
|
115
737
|
|
|
116
|
-
let value =
|
|
738
|
+
let value = td.getAttribute('data-numeric');
|
|
117
739
|
|
|
118
740
|
value = value.replace('£','');
|
|
119
741
|
value = value.replace('%','');
|
|
742
|
+
value = value.replace(',','');
|
|
120
743
|
value = Number.parseInt(value);
|
|
121
744
|
|
|
122
745
|
total += value;
|
|
746
|
+
pieCount++;
|
|
123
747
|
}
|
|
124
748
|
});
|
|
125
749
|
|
|
126
|
-
|
|
750
|
+
// Create the paths
|
|
751
|
+
Array.from(item.querySelectorAll('td')).forEach((td:any, subindex) => {
|
|
127
752
|
|
|
128
|
-
|
|
753
|
+
const display = getComputedStyle(td).display;
|
|
754
|
+
|
|
755
|
+
if (subindex != 0 && pieCount == 1 && display != "none"){
|
|
756
|
+
|
|
757
|
+
const pathData = `M 0 0 L 100 0 A 100 100 0 1 1 100 -0.01 L 0 0`;
|
|
758
|
+
|
|
759
|
+
paths += `<path d="${pathData}" style="${td.getAttribute('style')} --path-index: ${subindex};"></path>`;
|
|
760
|
+
tooltips += `<foreignObject x="-70" y="-70" width="140" height="140" ><div><span class="h5 mb-0"><span class="total d-block">${ucfirst(unsnake(title))}</span> ${ucfirst(unsnake(td.getAttribute('data-label')))}<br/> ${td.innerHTML}${td.hasAttribute('data-second') ? `${td.getAttribute('data-second-label')}: ${td.getAttribute('data-second')}` : ''}</span></div></foreignObject>`;
|
|
761
|
+
}
|
|
762
|
+
else if(subindex != 0){
|
|
129
763
|
|
|
130
|
-
let value =
|
|
764
|
+
let value = td.getAttribute('data-numeric');
|
|
765
|
+
let hide = display == "none" ? "display: none;" : "";
|
|
131
766
|
|
|
132
767
|
value = value.replace('£','');
|
|
133
768
|
value = value.replace('%','');
|
|
769
|
+
value = value.replace(',','');
|
|
134
770
|
value = Number.parseInt(value);
|
|
135
771
|
|
|
136
772
|
let percent = value/total;
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
const
|
|
140
|
-
|
|
141
|
-
|
|
773
|
+
const [startX, startY] = getCoordinatesForPercent(cumulativePercent,pieCount);
|
|
774
|
+
const [endX, endY] = getCoordinatesForPercent(cumulativePercent+percent,pieCount);
|
|
775
|
+
const largeArcFlag = percent > .5 ? 1 : 0; // if the slice is more than 50%, take the large arc (the long way around)
|
|
776
|
+
const pathData = [
|
|
777
|
+
`M 0 0`,
|
|
778
|
+
`L ${(startX ? startX.toFixed(0): 0)} ${(startY ? startY.toFixed(0): 0)}`, // Move
|
|
779
|
+
`A 100 100 0 ${largeArcFlag} 1 ${(endX ? endX.toFixed(0) : 0)} ${(endY ? endY.toFixed(0) : 0)}`, // Arc
|
|
780
|
+
`L 0 0`, // Line
|
|
781
|
+
].join(' ');
|
|
782
|
+
|
|
783
|
+
paths += `<path d="${pathData}" style="${td.getAttribute('style')} --path-index: ${subindex};${hide}"></path>`;
|
|
784
|
+
tooltips += `<foreignObject x="-70" y="-70" width="140" height="140" ><div><span class="h5 mb-0"><span class="total d-block">${ucfirst(unsnake(title))}</span> ${ucfirst(unsnake(td.getAttribute('data-label')))}<br/> ${td.innerHTML}${td.hasAttribute('data-second') ? `${td.getAttribute('data-second-label')}: ${td.getAttribute('data-second')}` : ''}</span></div></foreignObject>`;
|
|
785
|
+
|
|
786
|
+
// each slice starts where the last slice ended, so keep a cumulative percent
|
|
787
|
+
if(display != 'none')
|
|
142
788
|
cumulativePercent += percent;
|
|
143
|
-
|
|
144
|
-
const [endX, endY] = getCoordinatesForPercent(cumulativePercent);
|
|
145
|
-
|
|
146
|
-
// if the slice is more than 50%, take the large arc (the long way around)
|
|
147
|
-
const largeArcFlag = percent > .5 ? 1 : 0;
|
|
148
|
-
|
|
149
|
-
// create an array and join it just for code readability
|
|
150
|
-
const pathData = [
|
|
151
|
-
`M ${startX} ${startY}`, // Move
|
|
152
|
-
`A 100 100 0 ${largeArcFlag} 1 ${endX} ${endY}`, // Arc
|
|
153
|
-
`L 0 0`, // Line
|
|
154
|
-
].join(' ');
|
|
155
|
-
|
|
156
|
-
paths += `<path d="${pathData}"></path>`;
|
|
157
|
-
tooltips += `<foreignObject x="-70" y="-70" width="140" height="140" style="transform: rotate(90deg)"><div><span class="h5 mb-0"><span class="total d-block">${ucfirst(unsnake(title))}</span> ${ucfirst(unsnake(cell.getAttribute('data-label')))}<br/> ${cell.innerHTML}</span></div></foreignObject>`;
|
|
158
789
|
}
|
|
159
790
|
});
|
|
160
|
-
|
|
161
|
-
returnString += `<div class="pie"><svg viewBox="-105 -105 210 210"
|
|
791
|
+
|
|
792
|
+
returnString += `<div class="pie"><svg viewBox="-105 -105 210 210" preserveAspectRatio="none" style="--row-index: ${index+1};">${paths}${tooltips}</svg><div><span class="h5 mb-0">${title}</span></div></div>`
|
|
162
793
|
});
|
|
163
794
|
|
|
164
795
|
pieWrapper.innerHTML = returnString;
|
|
165
796
|
}
|
|
166
797
|
|
|
167
|
-
export const
|
|
798
|
+
export const createSlope = function(chartElement:any,chartOuter:any){
|
|
799
|
+
let n:number = 0;
|
|
800
|
+
let totalX:number = 0;
|
|
801
|
+
let totalY:number = 0;
|
|
802
|
+
let totalXY:number = 0;
|
|
803
|
+
let totalXsquared:number = 0;
|
|
168
804
|
|
|
169
|
-
let
|
|
170
|
-
let linesWrapper = chartElement.querySelector('.lines');
|
|
805
|
+
let {min,max,start,end,slope,yInt} = getChartData(chartElement,chartOuter);
|
|
171
806
|
|
|
172
|
-
let
|
|
807
|
+
let chart = chartOuter.querySelector('.chart');
|
|
808
|
+
let slopeWrapper = chartOuter.querySelector('.slope');
|
|
173
809
|
|
|
174
|
-
|
|
175
|
-
let spacer = 200/(items.length - 1);
|
|
810
|
+
if(!slopeWrapper){
|
|
176
811
|
|
|
177
|
-
|
|
178
|
-
|
|
812
|
+
slopeWrapper = document.createElement("div");
|
|
813
|
+
slopeWrapper.setAttribute('class','slope');
|
|
814
|
+
chart.prepend(slopeWrapper);
|
|
815
|
+
}
|
|
179
816
|
|
|
180
|
-
|
|
817
|
+
Array.from(chart.querySelectorAll('tbody tr')).forEach((tr:any) => {
|
|
818
|
+
|
|
819
|
+
const display = getComputedStyle(tr).display;
|
|
820
|
+
if(display != "none"){
|
|
821
|
+
|
|
822
|
+
let x = parseFloat(tr.querySelector('td:first-child').textContent);
|
|
823
|
+
let y = 0;
|
|
181
824
|
|
|
182
|
-
|
|
825
|
+
Array.from(tr.querySelectorAll('td:not(:first-child)')).forEach((td:any) => {
|
|
826
|
+
y += parseFloat(td.getAttribute('data-numeric'));
|
|
827
|
+
});
|
|
828
|
+
|
|
829
|
+
let xy = x * y;
|
|
830
|
+
let xSquared = x * x;
|
|
831
|
+
|
|
832
|
+
totalX += x;
|
|
833
|
+
totalY += y;
|
|
834
|
+
totalXY += xy;
|
|
835
|
+
totalXsquared += xSquared;
|
|
836
|
+
|
|
837
|
+
n++;
|
|
183
838
|
}
|
|
184
839
|
});
|
|
185
840
|
|
|
186
|
-
// populate the lines array from the items array
|
|
187
|
-
Array.from(chartElement.querySelectorAll('tbody tr')).forEach((item, index) => {
|
|
188
841
|
|
|
189
|
-
|
|
842
|
+
// Least squares method (https://www.youtube.com/watch?v=P8hT5nDai6A)
|
|
843
|
+
let m = slope ? parseFloat(slope) : ((n * totalXY) - (totalX * totalY)) / ((n * totalXsquared) - (totalX * totalX)); // Slope
|
|
844
|
+
let b = yInt ? parseFloat(yInt) : (totalY - (m * totalX)) / n; // Y intercept
|
|
190
845
|
|
|
191
|
-
|
|
846
|
+
let firstY = (m * parseFloat(start)) + b;
|
|
847
|
+
let lastY = (m * parseFloat(end)) + b;
|
|
848
|
+
|
|
849
|
+
let { percent: firstYPercent } = getValues(firstY,min,max);
|
|
850
|
+
let { percent: lastYPercent } = getValues(lastY,min,max);
|
|
192
851
|
|
|
193
|
-
|
|
852
|
+
slopeWrapper.innerHTML = `<svg viewBox="0 0 200 100" class="line" preserveAspectRatio="none"><path fill="none" d="M 0 ${100-firstYPercent} L 200 ${100-lastYPercent}" style="--path: path('M 0 100 L 200 100');"></path></svg>`;
|
|
853
|
+
}
|
|
194
854
|
|
|
195
|
-
|
|
196
|
-
value = value.replace('%','');
|
|
197
|
-
value = Number.parseFloat(value) - min;
|
|
855
|
+
function createKeyTotals(chartElement:any,chartOuter:any){
|
|
198
856
|
|
|
199
|
-
|
|
857
|
+
let chartTotal = 0;
|
|
200
858
|
|
|
201
|
-
|
|
859
|
+
Array.from(chartOuter.querySelectorAll('tbody tr:not([data-total]) td[data-numeric]:not([data-label="Min"]):not([data-label="Max"]):not(:first-child)')).forEach((td:any) => {
|
|
202
860
|
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
});
|
|
861
|
+
const value = Number.parseFloat(td.getAttribute('data-numeric'));
|
|
862
|
+
chartTotal += value;
|
|
206
863
|
});
|
|
864
|
+
// Get row totals already worked out
|
|
865
|
+
Array.from(chartOuter.querySelectorAll('tbody tr[data-total]')).forEach((tr:any) => {
|
|
207
866
|
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
returnString += `
|
|
211
|
-
<svg viewBox="0 0 200 100" class="line" preserveAspectRatio="none">
|
|
212
|
-
<path fill="none" d="${line}"></path>
|
|
213
|
-
</svg>`
|
|
867
|
+
const value = Number.parseFloat(tr.getAttribute('data-total'));
|
|
868
|
+
chartTotal += value;
|
|
214
869
|
});
|
|
215
870
|
|
|
216
|
-
|
|
871
|
+
chartElement.setAttribute('data-total',chartTotal);
|
|
872
|
+
|
|
873
|
+
Array.from(chartOuter.querySelectorAll('.chart__key .key[data-label]')).forEach((key:any) => {
|
|
874
|
+
|
|
875
|
+
if(key.querySelector('.chart__total'))
|
|
876
|
+
key.querySelector('.chart__total').remove();
|
|
877
|
+
|
|
878
|
+
let label = key.getAttribute('data-label');
|
|
879
|
+
let keyTotal:any = 0;
|
|
880
|
+
|
|
881
|
+
Array.from(chartOuter.querySelectorAll(`tbody td[data-label="${label}"]`)).forEach((td:any) => {
|
|
882
|
+
|
|
883
|
+
const value = Number.parseFloat(td.getAttribute('data-numeric'));
|
|
884
|
+
keyTotal += value;
|
|
885
|
+
});
|
|
886
|
+
|
|
887
|
+
let keyPercent = Math.round((keyTotal/chartTotal)*100);
|
|
888
|
+
|
|
889
|
+
if(chartElement.hasAttribute('data-currency')){
|
|
890
|
+
|
|
891
|
+
if (chartElement.getAttribute('data-currency') == "GBP") {
|
|
892
|
+
// @ts-ignore
|
|
893
|
+
keyTotal = new Intl.NumberFormat('en-GB', { style: 'currency', currency: 'GBP', trailingZeroDisplay: 'stripIfInteger' }).format(keyTotal);
|
|
894
|
+
}
|
|
895
|
+
}
|
|
896
|
+
else if(chartElement.hasAttribute('data-total-format')){
|
|
897
|
+
keyTotal = chartElement.getAttribute('data-total-format').replace('{i}',keyTotal);
|
|
898
|
+
}
|
|
899
|
+
else {
|
|
900
|
+
keyTotal = new Intl.NumberFormat('en-GB').format(keyTotal);
|
|
901
|
+
}
|
|
902
|
+
|
|
903
|
+
key.innerHTML += `<span class="chart__total"><span class="chart__total__number"><span class="visually-hidden">Total: </span>${keyTotal}</span><span class="chart__total__percent"><span class="visually-hidden">Total percent: </span>${keyPercent}%</span></span>`;
|
|
904
|
+
});
|
|
217
905
|
}
|
|
906
|
+
// #endregion
|
|
218
907
|
|
|
219
|
-
export default
|
|
908
|
+
export default setupChart;
|