@iamproperty/components 5.5.0 → 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/header.css +1 -1
- package/assets/css/components/header.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.js +7 -0
- package/assets/js/components/address-lookup/address-lookup.component.min.js +4 -4
- package/assets/js/components/address-lookup/address-lookup.component.min.js.map +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 +2 -2
- 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/header.scss +8 -1
- 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/address-lookup/address-lookup.component.ts +9 -0
- 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 +397 -381
- package/dist/components.umd.js +60 -78
- 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/src/components/Header/Header.vue +1 -1
- package/src/components/Table/Table.vue +2 -2
- package/assets/sass/elements/badge.scss +0 -29
|
@@ -1,151 +1,653 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
1
|
+
import { ucfirst, unsnake, numberOfDays } from './helpers.js';
|
|
2
|
+
// #region Functions that setup and trigger other functions
|
|
3
|
+
export const setupChart = (chartElement, chartOuter, tableElement) => {
|
|
4
|
+
// #region Reset the chart
|
|
5
|
+
// empty divs to re-populate
|
|
6
|
+
const chartOptions = chartOuter.querySelector('.chart__options');
|
|
7
|
+
chartOptions.innerHTML = `<span>Chart Type</span>`;
|
|
8
|
+
const chartKey = chartOuter.querySelector('.chart__key');
|
|
9
|
+
chartKey.innerHTML = '';
|
|
10
|
+
const chartGuidelines = chartOuter.querySelector('.chart__guidelines');
|
|
11
|
+
chartGuidelines.innerHTML = ``;
|
|
12
|
+
const chartYaxis = chartOuter.querySelector('.chart__yaxis');
|
|
13
|
+
chartYaxis.innerHTML = ``;
|
|
14
|
+
// Remove old input fields
|
|
15
|
+
Array.from(chartOuter.querySelectorAll(':scope > input[type="checkbox"],:scope > input[type="radio"]')).map((element) => { element.remove(); });
|
|
16
|
+
// #endregion
|
|
17
|
+
createTypeSwitcher(chartElement, chartKey, chartOptions);
|
|
18
|
+
let { xaxis, type } = getChartData(chartElement, chartOuter);
|
|
19
|
+
setCellData(chartElement, chartOuter, tableElement);
|
|
20
|
+
createChartKey(chartOuter, tableElement, chartKey);
|
|
21
|
+
createChartGuidelines(chartElement, chartOuter, chartGuidelines);
|
|
22
|
+
createChartYaxis(chartElement, chartOuter, chartYaxis);
|
|
23
|
+
const availableTypes = chartElement.hasAttribute('data-types') ? chartElement.getAttribute('data-types').split(',') : [type];
|
|
24
|
+
if (availableTypes.includes('line')) {
|
|
25
|
+
createLines(chartElement, chartOuter);
|
|
26
|
+
}
|
|
27
|
+
if (availableTypes.includes('pie'))
|
|
28
|
+
createPies(chartOuter);
|
|
29
|
+
if (xaxis) {
|
|
30
|
+
createXaxis(chartElement, chartOuter, xaxis);
|
|
31
|
+
}
|
|
32
|
+
if (chartElement.hasAttribute('data-slope')) // Need to check attribute is there not its value
|
|
33
|
+
createSlope(chartElement, chartOuter);
|
|
34
|
+
if (chartElement.classList.contains('chart--show-totals'))
|
|
35
|
+
createKeyTotals(chartElement, chartOuter);
|
|
36
|
+
if (availableTypes.includes('bar') || availableTypes.includes('dumbbell') || availableTypes.includes('responsive'))
|
|
37
|
+
setLongestLabel(chartOuter);
|
|
38
|
+
// Event handlers
|
|
39
|
+
setEventHandlers(chartElement, chartOuter);
|
|
40
|
+
return true;
|
|
41
|
+
};
|
|
42
|
+
// #endregion
|
|
43
|
+
// #region Event handlers and observers
|
|
44
|
+
export const setEventHandlers = function (chartElement, chartOuter) {
|
|
45
|
+
const showData = chartOuter.querySelectorAll(':scope > input[type="checkbox"]');
|
|
46
|
+
let { type } = getChartData(chartElement, chartOuter);
|
|
47
|
+
const availableTypes = chartElement.hasAttribute('data-types') ? chartElement.getAttribute('data-types').split(',') : [type];
|
|
48
|
+
for (var i = 0; i < showData.length; i++) {
|
|
49
|
+
showData[i].addEventListener('change', function () {
|
|
50
|
+
if (availableTypes.includes('pie'))
|
|
51
|
+
createPies(chartOuter);
|
|
52
|
+
//setupOptionalContent(chartElement,min,max); // TODO: move this to the observer and just update the data attribute
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
/* TO DO: do i need?
|
|
56
|
+
// Update chart type
|
|
57
|
+
const chartTypes = chartElement.querySelectorAll(':scope > input[type="radio"]');
|
|
58
|
+
for (var i = 0; i < chartTypes.length; i++) {
|
|
59
|
+
chartTypes[i].addEventListener('change', function() {
|
|
60
|
+
//setupOptionalContent(chartElement,min,max); // TODO: move this to the observer and just update the data attribute
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
*/
|
|
64
|
+
};
|
|
65
|
+
export const setEventObservers = function (chartElement, chartOuter) {
|
|
66
|
+
if (chartElement.hasAttribute('data-series'))
|
|
67
|
+
return false;
|
|
68
|
+
let table = chartElement.querySelector('table');
|
|
69
|
+
let newTable = chartOuter.querySelector('table');
|
|
70
|
+
const attributesUpdated = (mutationList, observer) => {
|
|
71
|
+
observer.disconnect();
|
|
72
|
+
observer2.disconnect();
|
|
73
|
+
for (const mutation of mutationList) {
|
|
74
|
+
if (mutation.attributeName == 'class' || (mutation.type === 'attributes' && mutation.attributeName === 'data-total') || mutation.type === 'attributes') {
|
|
75
|
+
newTable.innerHTML = table.innerHTML;
|
|
76
|
+
setupChart(chartElement, chartOuter, newTable);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
observer.observe(table, { characterData: true, subtree: true });
|
|
80
|
+
observer2.observe(chartElement, { attributes: true });
|
|
81
|
+
};
|
|
82
|
+
const tableUpdated = (mutationList, observer) => {
|
|
83
|
+
observer.disconnect();
|
|
84
|
+
observer2.disconnect();
|
|
85
|
+
for (const mutation of mutationList) {
|
|
86
|
+
if (mutation.type == "characterData" || (mutation.type == "childList" && mutation.addedNodes.length)) {
|
|
87
|
+
newTable.innerHTML = table.innerHTML;
|
|
88
|
+
setupChart(chartElement, chartOuter, newTable);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
observer.observe(table, { characterData: true, subtree: true });
|
|
92
|
+
observer2.observe(chartElement, { attributes: true });
|
|
93
|
+
};
|
|
94
|
+
let observer = new MutationObserver(tableUpdated);
|
|
95
|
+
let observer2 = new MutationObserver(attributesUpdated);
|
|
96
|
+
observer.observe(table, { characterData: true, subtree: true });
|
|
97
|
+
observer2.observe(chartElement, { attributes: true });
|
|
98
|
+
return true;
|
|
99
|
+
};
|
|
100
|
+
// #endregion
|
|
101
|
+
// #region GET functions
|
|
102
|
+
export const getChartData = function (chartElement, chartOuter) {
|
|
103
|
+
let table = chartOuter.querySelector('.chart__wrapper table');
|
|
104
|
+
let min = chartElement.hasAttribute('data-min') ? chartElement.getAttribute('data-min') : 0;
|
|
105
|
+
let max = chartElement.hasAttribute('data-max') ? chartElement.getAttribute('data-max') : getLargestValue(table);
|
|
106
|
+
let type = chartElement.hasAttribute('data-type') ? chartElement.getAttribute('data-type') : 'column';
|
|
107
|
+
let yaxis = chartElement.hasAttribute('data-yaxis') ? chartElement.getAttribute('data-yaxis').split(',') : [];
|
|
108
|
+
let guidelines = chartElement.hasAttribute('data-guidelines') ? chartElement.getAttribute('data-guidelines').split(',') : [];
|
|
109
|
+
let targets = chartElement.hasAttribute('data-targets') ? JSON.parse(chartElement.getAttribute('data-targets')) : null;
|
|
110
|
+
let events = chartElement.hasAttribute('data-events') ? JSON.parse(chartElement.getAttribute('data-events')) : null;
|
|
111
|
+
let xaxis = chartElement.hasAttribute('data-xaxis') ? chartElement.getAttribute('data-xaxis').split(',') : null;
|
|
112
|
+
let increment = chartElement.hasAttribute('data-increment') ? chartElement.getAttribute('data-increment') : null;
|
|
113
|
+
let start = chartElement.hasAttribute('data-start') ? chartElement.getAttribute('data-start') : 0;
|
|
114
|
+
let end = chartElement.hasAttribute('data-end') ? chartElement.getAttribute('data-end') : getLargestValue(table); // TODO - get largest value from the data-xaxis
|
|
115
|
+
let slope = chartElement.hasAttribute('data-slope') ? chartElement.getAttribute('data-slope') : null;
|
|
116
|
+
let yInt = chartElement.hasAttribute('data-yint') ? chartElement.getAttribute('data-yint') : null;
|
|
117
|
+
return { min, max, type, yaxis, targets, events, xaxis, increment, start, end, slope, yInt, guidelines };
|
|
118
|
+
};
|
|
119
|
+
function getLargestValue(table) {
|
|
120
|
+
let values = Array.from(table.querySelectorAll('tbody td:not(:first-child)')).map((element) => {
|
|
121
|
+
let currentValue = String(element.textContent);
|
|
122
|
+
currentValue = currentValue.replace('£', '');
|
|
123
|
+
currentValue = currentValue.replace('%', '');
|
|
124
|
+
currentValue = currentValue.replace(',', '');
|
|
125
|
+
currentValue = Number.parseFloat(currentValue);
|
|
126
|
+
return currentValue;
|
|
127
|
+
});
|
|
128
|
+
let largetValue = Math.max(...values);
|
|
129
|
+
// TO DO round to the nearest 10, 100, 1000 and so on
|
|
130
|
+
return Math.ceil(largetValue);
|
|
131
|
+
}
|
|
132
|
+
const getValues = function (value, min, max, start) {
|
|
133
|
+
let cleanValue = String(value);
|
|
134
|
+
cleanValue = cleanValue.replace('£', '');
|
|
135
|
+
cleanValue = cleanValue.replace('%', '');
|
|
136
|
+
cleanValue = cleanValue.replace(',', '');
|
|
137
|
+
cleanValue = Number.parseFloat(cleanValue);
|
|
138
|
+
let percent = ((cleanValue - min) / (max - min)) * 100;
|
|
139
|
+
let axis = percent;
|
|
140
|
+
let bottom = 0;
|
|
141
|
+
if (start && start != 0) {
|
|
142
|
+
bottom = ((start - min) / (max - min)) * 100;
|
|
143
|
+
}
|
|
144
|
+
// If the value is negative the position below the 0 line
|
|
145
|
+
if (min < 0) {
|
|
146
|
+
bottom = Math.abs(((min) / (max - min)) * 100);
|
|
147
|
+
if (cleanValue < 0) {
|
|
148
|
+
percent = bottom - percent;
|
|
149
|
+
bottom = bottom - percent;
|
|
150
|
+
axis = bottom;
|
|
151
|
+
}
|
|
152
|
+
else {
|
|
153
|
+
percent = percent - bottom;
|
|
154
|
+
axis = percent + bottom;
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
return { percent, axis, bottom };
|
|
158
|
+
};
|
|
159
|
+
function getCoordinatesForPercent(percent, pieCount) {
|
|
160
|
+
// This moves the start point to the top middle point like a clock
|
|
161
|
+
if (pieCount > 1)
|
|
162
|
+
percent = percent - 0.25;
|
|
163
|
+
const x = Math.cos(2 * Math.PI * percent);
|
|
164
|
+
const y = Math.sin(2 * Math.PI * percent);
|
|
165
|
+
return [x * 100, y * 100];
|
|
166
|
+
}
|
|
167
|
+
// #endregion
|
|
168
|
+
// #region SET functions - set data attributes and classes
|
|
169
|
+
export const setCellData = function (chartElement, chartOuter, table) {
|
|
170
|
+
let { min, max } = getChartData(chartElement, chartOuter);
|
|
171
|
+
let chartType = chartElement.getAttribute('data-type');
|
|
172
|
+
let increment = chartElement.getAttribute('data-increment');
|
|
173
|
+
let incrementStart = chartElement.getAttribute('data-start');
|
|
174
|
+
let incrementEnd = chartElement.getAttribute('data-end');
|
|
175
|
+
let startDay = min;
|
|
176
|
+
// Change how gant charts are configured as this just seems bizarre now
|
|
177
|
+
if (increment == "days") {
|
|
178
|
+
max = numberOfDays(min, max);
|
|
179
|
+
min = 0;
|
|
180
|
+
chartElement.querySelector('tbody').setAttribute('style', `--single-day:${((1 / max) * 100)}%;`);
|
|
181
|
+
}
|
|
182
|
+
Array.from(table.querySelectorAll('tbody tr')).forEach((tr, index) => {
|
|
26
183
|
let group = tr.querySelector('td:first-child, th:first-child') ? tr.querySelector('td:first-child, th:first-child').innerHTML : '';
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
184
|
+
let coverageStart = 100;
|
|
185
|
+
let coverageEnd = 0;
|
|
186
|
+
let cumulativeComparison = 0;
|
|
187
|
+
// For waffle charts
|
|
188
|
+
let previousAfter = 0;
|
|
189
|
+
let rowPosition = 0;
|
|
190
|
+
let totalPercent = 0;
|
|
191
|
+
// Set the data numeric value if not set
|
|
192
|
+
Array.from(tr.querySelectorAll('td:not([data-numeric]):not(:first-child)')).forEach((td) => {
|
|
193
|
+
let value = parseFloat(td.textContent.replace('£', '').replace('%', '').replace(',', ''));
|
|
194
|
+
let start = 0;
|
|
195
|
+
if (increment == "days") {
|
|
196
|
+
let dates = td.textContent.split(' - ');
|
|
197
|
+
if (dates[1]) {
|
|
198
|
+
value = numberOfDays(dates[0], dates[1]);
|
|
199
|
+
start = numberOfDays(startDay, dates[0]) - 1;
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
td.setAttribute('data-numeric', value);
|
|
203
|
+
td.setAttribute('data-start', start);
|
|
204
|
+
});
|
|
205
|
+
// Set the data label value if not set
|
|
206
|
+
Array.from(tr.querySelectorAll('td:not([data-label])')).forEach((td, index) => {
|
|
207
|
+
td.setAttribute('data-label', table.querySelectorAll('thead th')[index].textContent);
|
|
208
|
+
});
|
|
209
|
+
if (tr.querySelector('[data-label="Total"]')) {
|
|
210
|
+
tr.setAttribute('data-total', tr.querySelector('[data-label="Total"][data-numeric]').getAttribute('data-numeric'));
|
|
211
|
+
}
|
|
212
|
+
if (tr.querySelector('[data-label="Min"]')) {
|
|
213
|
+
tr.setAttribute('data-min', tr.querySelector('[data-label="Min"][data-numeric]').getAttribute('data-numeric'));
|
|
214
|
+
}
|
|
215
|
+
if (tr.querySelector('[data-label="Max"]')) {
|
|
216
|
+
tr.setAttribute('data-max', tr.querySelector('[data-label="Max"][data-numeric]').getAttribute('data-numeric'));
|
|
217
|
+
}
|
|
218
|
+
if ((chartType == "proportional" || chartType == "waffle") && !tr.hasAttribute('data-total')) {
|
|
219
|
+
let total = 0;
|
|
220
|
+
Array.from(tr.querySelectorAll('td[data-numeric]:not(:first-child)')).forEach((td) => {
|
|
221
|
+
let display = getComputedStyle(td).display;
|
|
222
|
+
if (display == 'none')
|
|
223
|
+
return;
|
|
224
|
+
total += Number.parseFloat(td.getAttribute('data-numeric'));
|
|
225
|
+
});
|
|
226
|
+
tr.setAttribute('data-total', total);
|
|
227
|
+
}
|
|
228
|
+
let rowMin = tr.hasAttribute('data-min') ? tr.getAttribute('data-min') : min;
|
|
229
|
+
let rowMax = tr.hasAttribute('data-max') ? tr.getAttribute('data-max') : max;
|
|
230
|
+
// Add a useful index css var for the use of animatons.
|
|
231
|
+
tr.setAttribute('style', `--row-index:${index + 1};`);
|
|
232
|
+
if (rowMin < 0) {
|
|
233
|
+
let minBottom = Math.abs(((rowMin) / (rowMax - rowMin)) * 100);
|
|
234
|
+
chartElement.setAttribute('style', `--min-bottom: ${minBottom}%;`);
|
|
235
|
+
}
|
|
236
|
+
// Add css vars to cells
|
|
237
|
+
Array.from(tr.querySelectorAll('td[data-numeric]:not([data-label="Min"]):not([data-label="Max"]):not(:first-child)')).forEach((td) => {
|
|
238
|
+
let display = getComputedStyle(td).display;
|
|
239
|
+
if (display == 'none')
|
|
240
|
+
return;
|
|
31
241
|
const label = td.getAttribute('data-label');
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
242
|
+
const content = td.innerHTML;
|
|
243
|
+
const value = Number.parseFloat(td.getAttribute('data-numeric'));
|
|
244
|
+
const start = Number.parseFloat(td.getAttribute('data-start'));
|
|
245
|
+
if (!td.querySelector('span[data-group]'))
|
|
246
|
+
td.innerHTML = `<span data-group="${group}" data-label="${label}">${content}</span>`;
|
|
247
|
+
if (!td.hasAttribute('style')) {
|
|
248
|
+
let { percent, bottom, axis } = getValues(value, rowMin, rowMax, start);
|
|
249
|
+
td.setAttribute('data-percent', percent);
|
|
250
|
+
td.setAttribute("style", `--bottom:${bottom}%;--percent:${percent}%;--axis:${axis}%;`);
|
|
251
|
+
if (tr.hasAttribute('data-total')) {
|
|
252
|
+
let rowTotal = tr.getAttribute('data-total');
|
|
253
|
+
let comparison = ((value - rowMin) / (rowTotal)) * 100;
|
|
254
|
+
cumulativeComparison += comparison;
|
|
255
|
+
td.setAttribute('data-comparison', comparison);
|
|
256
|
+
td.style.setProperty('--cumulative-comparision', `${cumulativeComparison}%`);
|
|
257
|
+
td.style.setProperty('--comparison', `${comparison}%`);
|
|
258
|
+
}
|
|
259
|
+
if (chartElement.classList.contains("chart--value-order")) {
|
|
260
|
+
let order = (10000 - Math.round(percent * 100));
|
|
261
|
+
td.style.setProperty('--order', `${order}%`);
|
|
262
|
+
}
|
|
263
|
+
if (chartType == "dumbbell") {
|
|
264
|
+
if (percent < coverageStart) {
|
|
265
|
+
tr.style.setProperty('--coverage-start', `${percent}%`);
|
|
266
|
+
coverageStart = percent;
|
|
267
|
+
}
|
|
268
|
+
if (percent > coverageEnd) {
|
|
269
|
+
tr.style.setProperty('--coverage-end', `${percent}%`);
|
|
270
|
+
coverageEnd = percent;
|
|
271
|
+
}
|
|
38
272
|
}
|
|
273
|
+
if (chartType == "waffle") {
|
|
274
|
+
let actualPercent = Math.round(td.getAttribute('data-comparison'));
|
|
275
|
+
// Prevent the chart from spilling out of the top
|
|
276
|
+
totalPercent += actualPercent;
|
|
277
|
+
if (totalPercent > 100)
|
|
278
|
+
actualPercent = actualPercent - (totalPercent - 100);
|
|
279
|
+
let percentMinusAfter = previousAfter != 0 ? actualPercent - (10 - previousAfter) : actualPercent;
|
|
280
|
+
let rowHeight = percentMinusAfter < 10 ? 10 : Math.floor(percentMinusAfter / 10) * 10;
|
|
281
|
+
let rowWidth = percentMinusAfter < 10 ? percentMinusAfter * 10 : 100;
|
|
282
|
+
let maxWidth = actualPercent * 10;
|
|
283
|
+
td.style.setProperty('--rowPosition', `${rowPosition}%`);
|
|
284
|
+
td.style.setProperty('--rowHeight', `${rowHeight}%`);
|
|
285
|
+
td.style.setProperty('--rowWidth', `${rowWidth}%`);
|
|
286
|
+
td.style.setProperty('--maxWidth', `${maxWidth}%`);
|
|
287
|
+
// Create the psuedo element variables for the the block that sticks out BELOW the main row
|
|
288
|
+
let beforeWidth = 0;
|
|
289
|
+
if (previousAfter != 0) {
|
|
290
|
+
beforeWidth = 100 - (previousAfter * 10);
|
|
291
|
+
td.style.setProperty('--beforeWidth', `${beforeWidth}%`);
|
|
292
|
+
td.style.setProperty('--beforeHeight', `${10 / rowHeight * 100}%`);
|
|
293
|
+
td.style.setProperty('--beforeLeft', `${previousAfter * 10}%`);
|
|
294
|
+
}
|
|
295
|
+
// Create the psuedo element variables for the the block that sticks out ABOVE the main row
|
|
296
|
+
let afterWidth = Math.round(percentMinusAfter - rowHeight) * 10;
|
|
297
|
+
let afterHeight = 10 / (rowHeight) * 100;
|
|
298
|
+
td.style.setProperty('--afterWidth', `${afterWidth}%`);
|
|
299
|
+
td.style.setProperty('--afterHeight', `${afterHeight}%`);
|
|
300
|
+
// If the row width plus the previous after is under 10 it needs to be added to the new previousAfter variable
|
|
301
|
+
if (previousAfter + beforeWidth / 10 + rowWidth / 10 < 10)
|
|
302
|
+
previousAfter += beforeWidth / 10 + (rowWidth / 10);
|
|
303
|
+
else if (percentMinusAfter < 10)
|
|
304
|
+
previousAfter = percentMinusAfter;
|
|
305
|
+
else
|
|
306
|
+
previousAfter = afterWidth / 10;
|
|
307
|
+
// Add to the row position so that the new row is shoved up if needed
|
|
308
|
+
rowPosition += (rowWidth > 0 ? rowHeight : 0) + (afterWidth > 0 ? 10 : 0);
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
// totals
|
|
312
|
+
if (chartElement.classList.contains('chart--show-totals')) {
|
|
313
|
+
let chartTotal = chartElement.getAttribute('data-total') ? Number.parseFloat(chartElement.getAttribute('data-total')) : 0;
|
|
314
|
+
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;
|
|
315
|
+
if (chartElement.querySelector(`.key[data-label="${label}"]`))
|
|
316
|
+
chartElement.querySelector(`.key[data-label="${label}"]`).setAttribute('data-total', keyTotal + value);
|
|
317
|
+
chartElement.setAttribute('data-total', chartTotal + value);
|
|
39
318
|
}
|
|
40
|
-
td.setAttribute("style", `--bottom:${bottom}%;--percent:${percent}%;`);
|
|
41
|
-
td.innerHTML = `<span data-group="${group}" data-label="${label}">${content}</span>`;
|
|
42
319
|
});
|
|
320
|
+
// Values for incremental charts i.e. histograms...
|
|
321
|
+
if (increment && incrementStart && incrementEnd) {
|
|
322
|
+
let firstCellValue = parseFloat(tr.querySelector('td:first-child').textContent.replace('£', '').replace('%', '').replace(',', ''));
|
|
323
|
+
let position = ((firstCellValue - incrementStart) / (incrementEnd - incrementStart)) * 100;
|
|
324
|
+
tr.setAttribute('style', `--position:${position}%;`);
|
|
325
|
+
}
|
|
43
326
|
});
|
|
44
|
-
}
|
|
45
|
-
export const
|
|
46
|
-
let
|
|
47
|
-
|
|
48
|
-
|
|
327
|
+
};
|
|
328
|
+
export const setLongestLabel = function (chartOuter) {
|
|
329
|
+
let chartWrapper = chartOuter.querySelector('.chart__wrapper');
|
|
330
|
+
let table = chartOuter.querySelector('.chart table');
|
|
331
|
+
// set the longest label attr so that the bar chart knows what margin to set on the left
|
|
332
|
+
let longestLabel = '';
|
|
333
|
+
Array.from(table.querySelectorAll('tbody tr td:first-child')).forEach((td) => {
|
|
334
|
+
if (typeof td.textContent != "undefined" && td.textContent.length > longestLabel.length)
|
|
335
|
+
longestLabel = td.textContent;
|
|
49
336
|
});
|
|
337
|
+
chartWrapper.setAttribute('data-longest-label', longestLabel);
|
|
338
|
+
// set the longest data set attr so that the bar chart knows what margin to set on the left
|
|
339
|
+
let longestSet = '';
|
|
340
|
+
Array.from(table.querySelectorAll('thead tr th')).forEach((td) => {
|
|
341
|
+
if (td.textContent.length > longestSet.length)
|
|
342
|
+
longestSet = td.textContent;
|
|
343
|
+
});
|
|
344
|
+
chartWrapper.setAttribute('data-set-label', longestSet);
|
|
50
345
|
};
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
const
|
|
55
|
-
|
|
56
|
-
|
|
346
|
+
// #endregion
|
|
347
|
+
// #region CREATE function
|
|
348
|
+
export const createTypeSwitcher = function (chartElement, chartKey, chartOptions) {
|
|
349
|
+
const chartID = `chart-${Date.now() + (Math.floor(Math.random() * 100) + 1)}`;
|
|
350
|
+
const availableTypes = chartElement.hasAttribute('data-types') ? chartElement.getAttribute('data-types').split(',') : [];
|
|
351
|
+
if (!chartElement.hasAttribute('data-types') && chartElement.hasAttribute('data-type'))
|
|
352
|
+
chartKey.insertAdjacentHTML('afterend', `<input type="radio" name="chart-type" value="${chartElement.getAttribute('data-type')}" checked="">`);
|
|
353
|
+
else if (chartElement.hasAttribute('data-types')) {
|
|
354
|
+
let chartType = chartElement.hasAttribute('data-type') ? chartElement.getAttribute('data-type') : 'column';
|
|
355
|
+
chartOptions.insertAdjacentHTML('beforebegin', availableTypes.map((type) => `<input type="radio" name="chart-type" value="${type}" id="${chartID}-${type}" ${chartType == type ? 'checked=""' : ''}>`).join(''));
|
|
356
|
+
chartOptions.insertAdjacentHTML('beforeend', availableTypes.map((type) => `<label for="${chartID}-${type}">${type}</label>`).join(''));
|
|
357
|
+
}
|
|
57
358
|
};
|
|
58
|
-
export const
|
|
59
|
-
|
|
60
|
-
const
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
359
|
+
export const createChartKey = function (chartOuter, tableElement, chartKey) {
|
|
360
|
+
const chartID = `chart-${Date.now() + (Math.floor(Math.random() * 100) + 1)}`;
|
|
361
|
+
//const chartOuter = chartElement.querySelector('.chart__outer');
|
|
362
|
+
let previousInput;
|
|
363
|
+
let headings = Array.from(tableElement.querySelectorAll('thead th'));
|
|
364
|
+
headings.forEach((arrayElement, index) => {
|
|
365
|
+
if (index != 0) {
|
|
366
|
+
previousInput = createChartKeyItem(chartID, index, arrayElement.textContent, chartKey, chartOuter, previousInput);
|
|
367
|
+
}
|
|
368
|
+
if (index == 50) {
|
|
369
|
+
headings.length = index + 1;
|
|
370
|
+
}
|
|
371
|
+
});
|
|
372
|
+
return true;
|
|
64
373
|
};
|
|
65
|
-
function
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
374
|
+
function createChartKeyItem(chartID, index, text, chartKey, chartOuter, previousInput) {
|
|
375
|
+
let input = document.createElement('input');
|
|
376
|
+
input.setAttribute('name', `${chartID}-dataset-${index}`);
|
|
377
|
+
input.setAttribute('id', `${chartID}-dataset-${index}`);
|
|
378
|
+
input.setAttribute('checked', `checked`);
|
|
379
|
+
input.setAttribute('type', `checkbox`);
|
|
380
|
+
if (index == 1)
|
|
381
|
+
chartOuter.prepend(input);
|
|
382
|
+
else
|
|
383
|
+
chartOuter.insertBefore(input, previousInput.nextSibling);
|
|
384
|
+
previousInput = input;
|
|
385
|
+
let label = document.createElement('label');
|
|
386
|
+
label.setAttribute('class', `key btn btn-action`);
|
|
387
|
+
label.setAttribute('for', `${chartID}-dataset-${index}`);
|
|
388
|
+
label.setAttribute('data-label', `${text}`);
|
|
389
|
+
label.innerHTML = `${text}`;
|
|
390
|
+
chartKey.append(label);
|
|
391
|
+
return previousInput;
|
|
69
392
|
}
|
|
70
|
-
export const
|
|
393
|
+
export const createChartGuidelines = function (chartElement, chartOuter, chartGuidelines) {
|
|
394
|
+
let { min, max, yaxis, increment, guidelines } = getChartData(chartElement, chartOuter);
|
|
395
|
+
if (guidelines.length)
|
|
396
|
+
yaxis = guidelines;
|
|
397
|
+
let startDay = min;
|
|
398
|
+
if (increment == "days") {
|
|
399
|
+
max = numberOfDays(min, max);
|
|
400
|
+
min = 0;
|
|
401
|
+
}
|
|
402
|
+
if (!chartGuidelines) {
|
|
403
|
+
chartGuidelines = document.createElement('div');
|
|
404
|
+
chartGuidelines.setAttribute('class', 'chart__guidelines');
|
|
405
|
+
}
|
|
406
|
+
chartGuidelines.innerHTML = '';
|
|
407
|
+
for (var i = 0; i < yaxis.length; i++) {
|
|
408
|
+
let value = parseFloat(yaxis[i].replace('£', '').replace('%', '').replace(',', ''));
|
|
409
|
+
if (increment == "days") {
|
|
410
|
+
value = numberOfDays(startDay, yaxis[i]) - 1;
|
|
411
|
+
}
|
|
412
|
+
let { axis } = getValues(value, min, max);
|
|
413
|
+
chartGuidelines.innerHTML += `<div class="guideline" style="--percent:${axis}%;"><span>${yaxis[i]}</span></div>`;
|
|
414
|
+
}
|
|
415
|
+
};
|
|
416
|
+
export const createChartYaxis = function (chartElement, chartOuter, chartYaxis) {
|
|
417
|
+
let { min, max, yaxis, increment } = getChartData(chartElement, chartOuter);
|
|
418
|
+
let startDay = min;
|
|
419
|
+
if (increment == "days") {
|
|
420
|
+
max = numberOfDays(min, max);
|
|
421
|
+
min = 0;
|
|
422
|
+
}
|
|
423
|
+
if (!chartYaxis) {
|
|
424
|
+
chartYaxis = document.createElement('div');
|
|
425
|
+
chartYaxis.setAttribute('class', 'chart__yaxis');
|
|
426
|
+
}
|
|
427
|
+
chartYaxis.innerHTML = '';
|
|
428
|
+
for (var i = 0; i < yaxis.length; i++) {
|
|
429
|
+
let value = parseFloat(yaxis[i].replace('£', '').replace('%', ''));
|
|
430
|
+
if (increment == "days") {
|
|
431
|
+
value = numberOfDays(startDay, yaxis[i]);
|
|
432
|
+
}
|
|
433
|
+
let { axis } = getValues(value, min, max);
|
|
434
|
+
chartYaxis.innerHTML += `<div class="axis__point" style="--percent:${axis}%;"><span>${yaxis[i]}</span></div>`;
|
|
435
|
+
}
|
|
436
|
+
};
|
|
437
|
+
export const createXaxis = function (chartElement, chartOuter, xaxis) {
|
|
438
|
+
const chart = chartOuter.querySelector('.chart');
|
|
439
|
+
let chartXaxis = chartOuter.querySelector('.chart__xaxis');
|
|
440
|
+
let { increment, start, end } = getChartData(chartElement, chartOuter);
|
|
441
|
+
if (!chartXaxis) {
|
|
442
|
+
chartXaxis = document.createElement('div');
|
|
443
|
+
chartXaxis.setAttribute('class', 'chart__xaxis');
|
|
444
|
+
}
|
|
445
|
+
if (increment && start && end) {
|
|
446
|
+
chartXaxis.innerHTML = '';
|
|
447
|
+
for (var i = 0; i < xaxis.length; i++) {
|
|
448
|
+
let value = parseFloat(xaxis[i].replace('£', '').replace('%', ''));
|
|
449
|
+
let position = ((value - start) / (end - start)) * 100;
|
|
450
|
+
chartXaxis.innerHTML += `<div class="axis__point" style="--percent:${position}%;"><span>${xaxis[i]}</span></div>`;
|
|
451
|
+
}
|
|
452
|
+
}
|
|
453
|
+
chart.prepend(chartXaxis);
|
|
454
|
+
};
|
|
455
|
+
export const createLines = function (chartElement, chartOuter) {
|
|
456
|
+
let { min, max } = getChartData(chartElement, chartOuter);
|
|
457
|
+
let chartType = chartElement.getAttribute('data-type');
|
|
458
|
+
let returnString = '';
|
|
459
|
+
//let chartWrapper = chartOuter.querySelector('.chart__wrapper');
|
|
460
|
+
let linesWrapper = chartOuter.querySelector('.chart__lines');
|
|
461
|
+
let items = Array.from(chartOuter.querySelectorAll('tbody tr'));
|
|
462
|
+
let lines = Array();
|
|
463
|
+
let linesCount = chartOuter.querySelectorAll('thead th:not(:first-child)').length;
|
|
464
|
+
let commands = Array();
|
|
465
|
+
let animatelines = Array();
|
|
466
|
+
let itemCount = items.length <= 1000 ? items.length : 1000;
|
|
467
|
+
let spacer = 200 / (itemCount - 1);
|
|
468
|
+
let spacerIndent = 0;
|
|
469
|
+
if (chartType == "combo") {
|
|
470
|
+
spacer = 200 / (itemCount);
|
|
471
|
+
spacerIndent = spacer / 2;
|
|
472
|
+
}
|
|
473
|
+
// Creates the lines array from the fields array
|
|
474
|
+
for (let i = 0; i < linesCount; i++) {
|
|
475
|
+
lines[i] = '';
|
|
476
|
+
animatelines[i] = '';
|
|
477
|
+
commands[i] = 'M';
|
|
478
|
+
}
|
|
479
|
+
// populate the lines array from the items array
|
|
480
|
+
let counter = 0;
|
|
481
|
+
Array.from(chartOuter.querySelectorAll('tbody tr')).forEach((item) => {
|
|
482
|
+
const display = getComputedStyle(item).display;
|
|
483
|
+
if (display != "none") {
|
|
484
|
+
Array.from(item.querySelectorAll('td:not(:first-child)')).forEach((cell, subindex) => {
|
|
485
|
+
if (!cell.classList.contains('chart__bar')) {
|
|
486
|
+
let value = cell.getAttribute('data-numeric');
|
|
487
|
+
let { axis } = getValues(value, min, max);
|
|
488
|
+
if (!Number.isNaN(axis)) {
|
|
489
|
+
lines[subindex] += `${commands[subindex]} ${(spacerIndent) + (spacer * counter)} ${100 - axis} `;
|
|
490
|
+
animatelines[subindex] += `${commands[subindex]} ${spacer * counter} 100 `;
|
|
491
|
+
commands[subindex] = 'L';
|
|
492
|
+
}
|
|
493
|
+
else {
|
|
494
|
+
commands[subindex] = 'M';
|
|
495
|
+
}
|
|
496
|
+
}
|
|
497
|
+
});
|
|
498
|
+
counter++;
|
|
499
|
+
}
|
|
500
|
+
});
|
|
501
|
+
lines.forEach((line, index) => {
|
|
502
|
+
returnString += `
|
|
503
|
+
<svg viewBox="0 0 200 100" class="line" preserveAspectRatio="none">
|
|
504
|
+
<path fill="none" d="${line}" style="--path: path('${animatelines[index]}');"></path>
|
|
505
|
+
</svg>`;
|
|
506
|
+
});
|
|
507
|
+
linesWrapper.innerHTML = returnString;
|
|
508
|
+
};
|
|
509
|
+
export const createPies = function (chartOuter) {
|
|
71
510
|
let returnString = '';
|
|
72
|
-
let
|
|
73
|
-
|
|
511
|
+
let chartInner = chartOuter.querySelector('.chart');
|
|
512
|
+
let pieWrapper = chartOuter.querySelector('.pies');
|
|
513
|
+
if (!pieWrapper) {
|
|
514
|
+
pieWrapper = document.createElement("div");
|
|
515
|
+
pieWrapper.setAttribute('class', 'pies');
|
|
516
|
+
chartInner.append(pieWrapper);
|
|
517
|
+
}
|
|
518
|
+
Array.from(chartInner.querySelectorAll('tbody tr')).forEach((item, index) => {
|
|
74
519
|
let paths = '';
|
|
75
520
|
let tooltips = '';
|
|
76
521
|
let cumulativePercent = 0;
|
|
77
522
|
let total = 0;
|
|
78
523
|
let titleKey = item.querySelectorAll('td')[0];
|
|
79
524
|
let title = titleKey.innerHTML;
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
525
|
+
let pieCount = 0;
|
|
526
|
+
// Work out the total amount
|
|
527
|
+
Array.from(item.querySelectorAll('td')).forEach((td, subindex) => {
|
|
528
|
+
const display = getComputedStyle(td).display;
|
|
529
|
+
if (subindex != 0 && display != 'none') {
|
|
530
|
+
let value = td.getAttribute('data-numeric');
|
|
83
531
|
value = value.replace('£', '');
|
|
84
532
|
value = value.replace('%', '');
|
|
533
|
+
value = value.replace(',', '');
|
|
85
534
|
value = Number.parseInt(value);
|
|
86
535
|
total += value;
|
|
536
|
+
pieCount++;
|
|
87
537
|
}
|
|
88
538
|
});
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
539
|
+
// Create the paths
|
|
540
|
+
Array.from(item.querySelectorAll('td')).forEach((td, subindex) => {
|
|
541
|
+
const display = getComputedStyle(td).display;
|
|
542
|
+
if (subindex != 0 && pieCount == 1 && display != "none") {
|
|
543
|
+
const pathData = `M 0 0 L 100 0 A 100 100 0 1 1 100 -0.01 L 0 0`;
|
|
544
|
+
paths += `<path d="${pathData}" style="${td.getAttribute('style')} --path-index: ${subindex};"></path>`;
|
|
545
|
+
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>`;
|
|
546
|
+
}
|
|
547
|
+
else if (subindex != 0) {
|
|
548
|
+
let value = td.getAttribute('data-numeric');
|
|
549
|
+
let hide = display == "none" ? "display: none;" : "";
|
|
92
550
|
value = value.replace('£', '');
|
|
93
551
|
value = value.replace('%', '');
|
|
552
|
+
value = value.replace(',', '');
|
|
94
553
|
value = Number.parseInt(value);
|
|
95
554
|
let percent = value / total;
|
|
96
|
-
|
|
97
|
-
const [
|
|
98
|
-
//
|
|
99
|
-
cumulativePercent += percent;
|
|
100
|
-
const [endX, endY] = getCoordinatesForPercent(cumulativePercent);
|
|
101
|
-
// if the slice is more than 50%, take the large arc (the long way around)
|
|
102
|
-
const largeArcFlag = percent > .5 ? 1 : 0;
|
|
103
|
-
// create an array and join it just for code readability
|
|
555
|
+
const [startX, startY] = getCoordinatesForPercent(cumulativePercent, pieCount);
|
|
556
|
+
const [endX, endY] = getCoordinatesForPercent(cumulativePercent + percent, pieCount);
|
|
557
|
+
const largeArcFlag = percent > .5 ? 1 : 0; // if the slice is more than 50%, take the large arc (the long way around)
|
|
104
558
|
const pathData = [
|
|
105
|
-
`M
|
|
106
|
-
`
|
|
559
|
+
`M 0 0`,
|
|
560
|
+
`L ${(startX ? startX.toFixed(0) : 0)} ${(startY ? startY.toFixed(0) : 0)}`,
|
|
561
|
+
`A 100 100 0 ${largeArcFlag} 1 ${(endX ? endX.toFixed(0) : 0)} ${(endY ? endY.toFixed(0) : 0)}`,
|
|
107
562
|
`L 0 0`, // Line
|
|
108
563
|
].join(' ');
|
|
109
|
-
paths += `<path d="${pathData}"></path>`;
|
|
110
|
-
tooltips += `<foreignObject x="-70" y="-70" width="140" height="140"
|
|
564
|
+
paths += `<path d="${pathData}" style="${td.getAttribute('style')} --path-index: ${subindex};${hide}"></path>`;
|
|
565
|
+
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>`;
|
|
566
|
+
// each slice starts where the last slice ended, so keep a cumulative percent
|
|
567
|
+
if (display != 'none')
|
|
568
|
+
cumulativePercent += percent;
|
|
111
569
|
}
|
|
112
570
|
});
|
|
113
|
-
returnString += `<div class="pie"><svg viewBox="-105 -105 210 210"
|
|
571
|
+
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>`;
|
|
114
572
|
});
|
|
115
573
|
pieWrapper.innerHTML = returnString;
|
|
116
574
|
};
|
|
117
|
-
export const
|
|
118
|
-
let
|
|
119
|
-
let
|
|
120
|
-
let
|
|
121
|
-
let
|
|
122
|
-
let
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
575
|
+
export const createSlope = function (chartElement, chartOuter) {
|
|
576
|
+
let n = 0;
|
|
577
|
+
let totalX = 0;
|
|
578
|
+
let totalY = 0;
|
|
579
|
+
let totalXY = 0;
|
|
580
|
+
let totalXsquared = 0;
|
|
581
|
+
let { min, max, start, end, slope, yInt } = getChartData(chartElement, chartOuter);
|
|
582
|
+
let chart = chartOuter.querySelector('.chart');
|
|
583
|
+
let slopeWrapper = chartOuter.querySelector('.slope');
|
|
584
|
+
if (!slopeWrapper) {
|
|
585
|
+
slopeWrapper = document.createElement("div");
|
|
586
|
+
slopeWrapper.setAttribute('class', 'slope');
|
|
587
|
+
chart.prepend(slopeWrapper);
|
|
588
|
+
}
|
|
589
|
+
Array.from(chart.querySelectorAll('tbody tr')).forEach((tr) => {
|
|
590
|
+
const display = getComputedStyle(tr).display;
|
|
591
|
+
if (display != "none") {
|
|
592
|
+
let x = parseFloat(tr.querySelector('td:first-child').textContent);
|
|
593
|
+
let y = 0;
|
|
594
|
+
Array.from(tr.querySelectorAll('td:not(:first-child)')).forEach((td) => {
|
|
595
|
+
y += parseFloat(td.getAttribute('data-numeric'));
|
|
596
|
+
});
|
|
597
|
+
let xy = x * y;
|
|
598
|
+
let xSquared = x * x;
|
|
599
|
+
totalX += x;
|
|
600
|
+
totalY += y;
|
|
601
|
+
totalXY += xy;
|
|
602
|
+
totalXsquared += xSquared;
|
|
603
|
+
n++;
|
|
127
604
|
}
|
|
128
605
|
});
|
|
129
|
-
//
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
606
|
+
// Least squares method (https://www.youtube.com/watch?v=P8hT5nDai6A)
|
|
607
|
+
let m = slope ? parseFloat(slope) : ((n * totalXY) - (totalX * totalY)) / ((n * totalXsquared) - (totalX * totalX)); // Slope
|
|
608
|
+
let b = yInt ? parseFloat(yInt) : (totalY - (m * totalX)) / n; // Y intercept
|
|
609
|
+
let firstY = (m * parseFloat(start)) + b;
|
|
610
|
+
let lastY = (m * parseFloat(end)) + b;
|
|
611
|
+
let { percent: firstYPercent } = getValues(firstY, min, max);
|
|
612
|
+
let { percent: lastYPercent } = getValues(lastY, min, max);
|
|
613
|
+
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>`;
|
|
614
|
+
};
|
|
615
|
+
function createKeyTotals(chartElement, chartOuter) {
|
|
616
|
+
let chartTotal = 0;
|
|
617
|
+
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) => {
|
|
618
|
+
const value = Number.parseFloat(td.getAttribute('data-numeric'));
|
|
619
|
+
chartTotal += value;
|
|
142
620
|
});
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
</svg>`;
|
|
621
|
+
// Get row totals already worked out
|
|
622
|
+
Array.from(chartOuter.querySelectorAll('tbody tr[data-total]')).forEach((tr) => {
|
|
623
|
+
const value = Number.parseFloat(tr.getAttribute('data-total'));
|
|
624
|
+
chartTotal += value;
|
|
148
625
|
});
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
626
|
+
chartElement.setAttribute('data-total', chartTotal);
|
|
627
|
+
Array.from(chartOuter.querySelectorAll('.chart__key .key[data-label]')).forEach((key) => {
|
|
628
|
+
if (key.querySelector('.chart__total'))
|
|
629
|
+
key.querySelector('.chart__total').remove();
|
|
630
|
+
let label = key.getAttribute('data-label');
|
|
631
|
+
let keyTotal = 0;
|
|
632
|
+
Array.from(chartOuter.querySelectorAll(`tbody td[data-label="${label}"]`)).forEach((td) => {
|
|
633
|
+
const value = Number.parseFloat(td.getAttribute('data-numeric'));
|
|
634
|
+
keyTotal += value;
|
|
635
|
+
});
|
|
636
|
+
let keyPercent = Math.round((keyTotal / chartTotal) * 100);
|
|
637
|
+
if (chartElement.hasAttribute('data-currency')) {
|
|
638
|
+
if (chartElement.getAttribute('data-currency') == "GBP") {
|
|
639
|
+
// @ts-ignore
|
|
640
|
+
keyTotal = new Intl.NumberFormat('en-GB', { style: 'currency', currency: 'GBP', trailingZeroDisplay: 'stripIfInteger' }).format(keyTotal);
|
|
641
|
+
}
|
|
642
|
+
}
|
|
643
|
+
else if (chartElement.hasAttribute('data-total-format')) {
|
|
644
|
+
keyTotal = chartElement.getAttribute('data-total-format').replace('{i}', keyTotal);
|
|
645
|
+
}
|
|
646
|
+
else {
|
|
647
|
+
keyTotal = new Intl.NumberFormat('en-GB').format(keyTotal);
|
|
648
|
+
}
|
|
649
|
+
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>`;
|
|
650
|
+
});
|
|
651
|
+
}
|
|
652
|
+
// #endregion
|
|
653
|
+
export default setupChart;
|