@pacem/pacem-charts 1.0.0-abel
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/LICENSE +191 -0
- package/NOTICE +4 -0
- package/dist/browser/pacem-charts.js +1623 -0
- package/dist/browser/pacem-charts.js.map +1 -0
- package/dist/browser/pacem-charts.min.js +2 -0
- package/dist/browser/pacem-charts.min.js.map +1 -0
- package/dist/bundle/pacem-charts.min.mjs +1 -0
- package/dist/bundle/pacem-charts.mjs +1462 -0
- package/dist/bundle/pacem-charts.mjs.map +7 -0
- package/dist/esm/column.js +357 -0
- package/dist/esm/generic.js +437 -0
- package/dist/esm/index-components-charts.js +4 -0
- package/dist/esm/index-components.js +1 -0
- package/dist/esm/index-iife.js +3 -0
- package/dist/esm/index.js +1 -0
- package/dist/esm/pie.js +306 -0
- package/dist/esm/types.js +504 -0
- package/package.json +38 -0
- package/typings/index.d.ts +239 -0
|
@@ -0,0 +1,1623 @@
|
|
|
1
|
+
(function (pacemFoundation, pacemCore) {
|
|
2
|
+
'use strict';
|
|
3
|
+
|
|
4
|
+
var __decorate$3 = (undefined && undefined.__decorate) || function (decorators, target, key, desc) {
|
|
5
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
6
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
7
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
8
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
9
|
+
};
|
|
10
|
+
//namespace Pacem.Components.Charts {
|
|
11
|
+
const MANAGED_EVENTS = ['keydown', 'keyup', 'click', 'dblclick', 'mouseover', 'mouseout', 'mouseenter', 'mouseleave', 'mousedown', 'mouseup', 'mousemove', 'contextmenu'];
|
|
12
|
+
//function logN(N, x) {
|
|
13
|
+
// return Math.log10(x) / Math.log10(N);
|
|
14
|
+
//}
|
|
15
|
+
const MSECS_PER_DAY = 1000 * 60 * 60 * 24;
|
|
16
|
+
const MAX_X_LABELS = 10;
|
|
17
|
+
const MAX_Y_LABELS = 10;
|
|
18
|
+
const SERIES_DATAITEM = 'pacem:chart-series:dataitem';
|
|
19
|
+
const SERIES_SERIES = 'pacem:chart-series:series';
|
|
20
|
+
function getEvenlySpaced(items, type, min, max, formatOrLang, labels = MAX_X_LABELS) {
|
|
21
|
+
const length = (type === 'number' || type === 'date') ? (max - min) : Math.max(1, items.length - 1);
|
|
22
|
+
const gaps = labels - 1;
|
|
23
|
+
var retval = [];
|
|
24
|
+
var step = 1;
|
|
25
|
+
for (var l = gaps; l > 1; l--) {
|
|
26
|
+
if ((length % l) === 0) {
|
|
27
|
+
step = length / l;
|
|
28
|
+
break;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
if (step === 1 /* coprime number? */ && length > labels) {
|
|
32
|
+
step = length;
|
|
33
|
+
}
|
|
34
|
+
const fnDate = typeof formatOrLang === 'function' ? formatOrLang : (step >= MSECS_PER_DAY ? ((d) => d.toLocaleDateString(formatOrLang)) : ((d) => d.toLocaleTimeString(formatOrLang)));
|
|
35
|
+
const fnDateFirst = typeof formatOrLang === 'function' ? formatOrLang : (step < MSECS_PER_DAY ? ((d) => d.toLocaleString(formatOrLang)) : fnDate);
|
|
36
|
+
const fnNum = typeof formatOrLang === 'function' ? formatOrLang : (n) => n.toLocaleString(formatOrLang);
|
|
37
|
+
for (var j = 0; j <= length; j += step) {
|
|
38
|
+
let label;
|
|
39
|
+
switch (type) {
|
|
40
|
+
case 'string':
|
|
41
|
+
if (j >= items.length) {
|
|
42
|
+
continue;
|
|
43
|
+
}
|
|
44
|
+
label = items[j].label;
|
|
45
|
+
break;
|
|
46
|
+
case 'number':
|
|
47
|
+
label = fnNum(min + j);
|
|
48
|
+
break;
|
|
49
|
+
case 'date':
|
|
50
|
+
let date = pacemCore.Utils.parseDate(min + j);
|
|
51
|
+
let fn = (j == 0) ? fnDateFirst : fnDate;
|
|
52
|
+
label = fn(date);
|
|
53
|
+
break;
|
|
54
|
+
default:
|
|
55
|
+
throw 'Not supported.';
|
|
56
|
+
}
|
|
57
|
+
retval.push(label);
|
|
58
|
+
}
|
|
59
|
+
return retval;
|
|
60
|
+
}
|
|
61
|
+
function getRoundedBoundaries(min, max, desiredSteps) {
|
|
62
|
+
desiredSteps ??= Math.min(bestGuessedDensity(min, max), MAX_Y_LABELS);
|
|
63
|
+
const roughStep = (max - min) / desiredSteps;
|
|
64
|
+
const magnitude = Math.pow(10, Math.floor(Math.log10(roughStep)));
|
|
65
|
+
const step = [1, 2, 5, 10].map(n => n * magnitude).filter(s => s >= roughStep).reduce((min, s) => Math.min(min, s), Number.POSITIVE_INFINITY);
|
|
66
|
+
return {
|
|
67
|
+
min: Math.floor(min / step) * step,
|
|
68
|
+
max: Math.ceil(max / step) * step,
|
|
69
|
+
round: step
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
// Deprecated
|
|
73
|
+
//function getRoundedBoundariesOld(a0: number, a1: number, steps?: number) {
|
|
74
|
+
// if (!(steps > 0)) {
|
|
75
|
+
// steps = bestGuessedDensity(a0, a1);
|
|
76
|
+
// }
|
|
77
|
+
// const magn = logN(steps, a1 - a0),
|
|
78
|
+
// exp = magn % 1 == 0 ? magn - 1 : magn,
|
|
79
|
+
// rounder = Math.pow(steps, Math.floor(exp));
|
|
80
|
+
// return {
|
|
81
|
+
// min: Math.floor(a0 / rounder) * rounder,
|
|
82
|
+
// max: Math.ceil(a1 / rounder) * rounder,
|
|
83
|
+
// round: rounder
|
|
84
|
+
// };
|
|
85
|
+
//}
|
|
86
|
+
function bestGuessedDensity(min, max) {
|
|
87
|
+
const delta = max - min;
|
|
88
|
+
if (!(delta > 0)) {
|
|
89
|
+
return 2;
|
|
90
|
+
}
|
|
91
|
+
const oom = Math.floor(Math.log10(delta));
|
|
92
|
+
const exp = Math.pow(10, -oom);
|
|
93
|
+
// bring everything to a single digit format
|
|
94
|
+
if (oom != 0) {
|
|
95
|
+
return bestGuessedDensity(min * exp, max * exp);
|
|
96
|
+
}
|
|
97
|
+
const capFn = (n) => {
|
|
98
|
+
const norm = Math.floor(Math.abs(n) * 10), limit = norm + 10 - (norm % 10);
|
|
99
|
+
return limit.roundoff();
|
|
100
|
+
};
|
|
101
|
+
const b = capFn(delta);
|
|
102
|
+
let a = 10;
|
|
103
|
+
if (min !== 0) {
|
|
104
|
+
a = capFn(Math.abs(max));
|
|
105
|
+
}
|
|
106
|
+
const ret = gcd(a, b);
|
|
107
|
+
return ret === 1 ? b : Math.max(ret, 10 - ret);
|
|
108
|
+
}
|
|
109
|
+
function gcd(a, b) {
|
|
110
|
+
if (a <= 0) {
|
|
111
|
+
return b;
|
|
112
|
+
}
|
|
113
|
+
return gcd(b % a, a);
|
|
114
|
+
}
|
|
115
|
+
class ChartEvent extends pacemCore.CustomUIEvent {
|
|
116
|
+
constructor(type, eventInit, originalEvent) {
|
|
117
|
+
super(type, eventInit, originalEvent);
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
let PacemChartSeriesElement = class PacemChartSeriesElement extends pacemCore.Components.PacemItemElement {
|
|
121
|
+
get values() {
|
|
122
|
+
return this.datasource;
|
|
123
|
+
}
|
|
124
|
+
};
|
|
125
|
+
__decorate$3([
|
|
126
|
+
pacemCore.Watch({ emit: true, converter: pacemCore.PropertyConverters.Json })
|
|
127
|
+
], PacemChartSeriesElement.prototype, "datasource", void 0);
|
|
128
|
+
__decorate$3([
|
|
129
|
+
pacemCore.Watch({ emit: true, converter: pacemCore.PropertyConverters.String })
|
|
130
|
+
], PacemChartSeriesElement.prototype, "label", void 0);
|
|
131
|
+
__decorate$3([
|
|
132
|
+
pacemCore.Watch({ emit: true, converter: pacemCore.PropertyConverters.String })
|
|
133
|
+
], PacemChartSeriesElement.prototype, "color", void 0);
|
|
134
|
+
__decorate$3([
|
|
135
|
+
pacemCore.Watch({ emit: true, converter: pacemCore.PropertyConverters.Boolean })
|
|
136
|
+
], PacemChartSeriesElement.prototype, "datapoints", void 0);
|
|
137
|
+
__decorate$3([
|
|
138
|
+
pacemCore.Watch( /* to be set programmatically (internally) */)
|
|
139
|
+
], PacemChartSeriesElement.prototype, "_uiElements", void 0);
|
|
140
|
+
PacemChartSeriesElement = __decorate$3([
|
|
141
|
+
pacemCore.CustomElement({ tagName: pacemCore.P + '-chart-series' })
|
|
142
|
+
], PacemChartSeriesElement);
|
|
143
|
+
const SVG_NS$2 = "http://www.w3.org/2000/svg";
|
|
144
|
+
class PacemSeriesChartElement extends pacemCore.Components.PacemItemsContainerElement {
|
|
145
|
+
constructor() {
|
|
146
|
+
super();
|
|
147
|
+
this._itemPropertyChangedCallback = (evt) => {
|
|
148
|
+
const propName = evt.detail.propertyName;
|
|
149
|
+
if (propName === 'datasource'
|
|
150
|
+
|| propName === 'label'
|
|
151
|
+
|| propName === 'datapoints'
|
|
152
|
+
|| propName === 'cssClass'
|
|
153
|
+
|| propName === 'color') {
|
|
154
|
+
this.draw();
|
|
155
|
+
}
|
|
156
|
+
};
|
|
157
|
+
this._series = [];
|
|
158
|
+
this._resizeHandler = (evt) => {
|
|
159
|
+
this._size = evt.detail;
|
|
160
|
+
this.draw();
|
|
161
|
+
};
|
|
162
|
+
// #endregion
|
|
163
|
+
this._broadcastHandler = (e) => {
|
|
164
|
+
/* Don't mess with the events:
|
|
165
|
+
* keep the original (trusted) events with their well-known names
|
|
166
|
+
* and provide a method to retrieve the 'ChartDataItem' based on the the eventTarget. */
|
|
167
|
+
const element = e.currentTarget;
|
|
168
|
+
const dataItem = pacemCore.CustomElementUtils.getAttachedPropertyValue(element, SERIES_DATAITEM);
|
|
169
|
+
if (pacemCore.Utils.isNull(dataItem)) {
|
|
170
|
+
return;
|
|
171
|
+
}
|
|
172
|
+
const chart = this, anchorPoint = chart.getAnchorPoint(element);
|
|
173
|
+
const evt = new ChartEvent('item' + e.type, { detail: { dataItem, anchorPoint } }, e);
|
|
174
|
+
const series = pacemCore.CustomElementUtils.getAttachedPropertyValue(element, SERIES_SERIES);
|
|
175
|
+
if (series instanceof PacemChartSeriesElement) {
|
|
176
|
+
series.dispatchEvent(evt);
|
|
177
|
+
}
|
|
178
|
+
chart.dispatchEvent(evt);
|
|
179
|
+
};
|
|
180
|
+
this._key = pacemCore.Utils.uniqueCode();
|
|
181
|
+
}
|
|
182
|
+
validate(item) {
|
|
183
|
+
return item instanceof PacemChartSeriesElement;
|
|
184
|
+
}
|
|
185
|
+
register(item) {
|
|
186
|
+
if (super.register(item)) {
|
|
187
|
+
item.addEventListener(pacemCore.PropertyChangeEventName, this._itemPropertyChangedCallback, false);
|
|
188
|
+
return true;
|
|
189
|
+
}
|
|
190
|
+
return false;
|
|
191
|
+
}
|
|
192
|
+
unregister(item) {
|
|
193
|
+
if (super.unregister(item)) {
|
|
194
|
+
item.removeEventListener(pacemCore.PropertyChangeEventName, this._itemPropertyChangedCallback, false);
|
|
195
|
+
return true;
|
|
196
|
+
}
|
|
197
|
+
return false;
|
|
198
|
+
}
|
|
199
|
+
propertyChangedCallback(name, old, val, first) {
|
|
200
|
+
super.propertyChangedCallback(name, old, val, first);
|
|
201
|
+
if (name === 'target') {
|
|
202
|
+
if (this._div != old) {
|
|
203
|
+
this._div && this._div.remove();
|
|
204
|
+
}
|
|
205
|
+
// reset
|
|
206
|
+
this._div = null;
|
|
207
|
+
this._body = null; // <- get checked on next draw
|
|
208
|
+
const eold = old;
|
|
209
|
+
if (eold) {
|
|
210
|
+
eold.classList.remove(pacemCore.PCSS + '-chart-area');
|
|
211
|
+
eold.innerHTML = '';
|
|
212
|
+
}
|
|
213
|
+
this._ensureResizer();
|
|
214
|
+
this.draw();
|
|
215
|
+
}
|
|
216
|
+
else if (!first) {
|
|
217
|
+
switch (name) {
|
|
218
|
+
case 'items':
|
|
219
|
+
this._databindAndDrawDebounced();
|
|
220
|
+
break;
|
|
221
|
+
case 'datasource':
|
|
222
|
+
this._databind();
|
|
223
|
+
// fall down to draw() call.
|
|
224
|
+
case 'yAxisDensity':
|
|
225
|
+
case 'xAxisDensity':
|
|
226
|
+
case 'xAxisType':
|
|
227
|
+
case 'yAxisMax':
|
|
228
|
+
case 'xAxisPosition':
|
|
229
|
+
case 'yAxisFormat':
|
|
230
|
+
this.draw();
|
|
231
|
+
break;
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
_databindAndDrawDebounced() {
|
|
236
|
+
cancelAnimationFrame(this._handle);
|
|
237
|
+
this._handle = requestAnimationFrame(() => {
|
|
238
|
+
this._databind();
|
|
239
|
+
this.draw();
|
|
240
|
+
});
|
|
241
|
+
}
|
|
242
|
+
_databind() {
|
|
243
|
+
this._datasource = pacemCore.Utils.isNullOrEmpty(this.datasource) ? this.items : this.datasource;
|
|
244
|
+
}
|
|
245
|
+
disconnectedCallback() {
|
|
246
|
+
this._div && this._div.remove();
|
|
247
|
+
const resizer = this._resizer;
|
|
248
|
+
if (!pacemCore.Utils.isNull(resizer)) {
|
|
249
|
+
resizer.removeEventListener(pacemCore.Components.ResizeEventName, this._resizeHandler, false);
|
|
250
|
+
resizer.remove();
|
|
251
|
+
this._resizer = null;
|
|
252
|
+
}
|
|
253
|
+
super.disconnectedCallback();
|
|
254
|
+
}
|
|
255
|
+
viewActivatedCallback() {
|
|
256
|
+
super.viewActivatedCallback();
|
|
257
|
+
this._ensureResizer();
|
|
258
|
+
this._databind();
|
|
259
|
+
this.draw();
|
|
260
|
+
}
|
|
261
|
+
_ensureResizer(target = this.target || this._div) {
|
|
262
|
+
if (pacemCore.Utils.isNull(this._resizer)) {
|
|
263
|
+
const shell = pacemCore.CustomElementUtils.findAncestorShell(this);
|
|
264
|
+
const resizer = this._resizer = shell.appendChild(new pacemCore.Components.PacemResizeElement());
|
|
265
|
+
resizer.addEventListener(pacemCore.Components.ResizeEventName, this._resizeHandler, false);
|
|
266
|
+
}
|
|
267
|
+
this._resizer.target = target;
|
|
268
|
+
}
|
|
269
|
+
// #region OO-scoped props
|
|
270
|
+
get chartSize() {
|
|
271
|
+
return this._size;
|
|
272
|
+
}
|
|
273
|
+
get chartSeries() {
|
|
274
|
+
return this._series;
|
|
275
|
+
}
|
|
276
|
+
get chartBody() {
|
|
277
|
+
return this._body;
|
|
278
|
+
}
|
|
279
|
+
get chartGrid() {
|
|
280
|
+
return this._grid;
|
|
281
|
+
}
|
|
282
|
+
get chartMask() {
|
|
283
|
+
return this._mask;
|
|
284
|
+
}
|
|
285
|
+
get chartKey() {
|
|
286
|
+
return this._key;
|
|
287
|
+
}
|
|
288
|
+
get chartContainer() {
|
|
289
|
+
return this._div;
|
|
290
|
+
}
|
|
291
|
+
assignUiBehaviors(ui, series, data, color) {
|
|
292
|
+
const alreadyRegistered = !pacemCore.Utils.isNull(pacemCore.CustomElementUtils.getAttachedPropertyValue(ui, SERIES_DATAITEM));
|
|
293
|
+
const item = pacemCore.Utils.extend({ series: series.label, color: color || series.color }, data);
|
|
294
|
+
pacemCore.CustomElementUtils.setAttachedPropertyValue(ui, SERIES_DATAITEM, item);
|
|
295
|
+
pacemCore.CustomElementUtils.setAttachedPropertyValue(ui, SERIES_SERIES, series);
|
|
296
|
+
if (!alreadyRegistered) {
|
|
297
|
+
MANAGED_EVENTS.forEach(type => {
|
|
298
|
+
ui.addEventListener(type, this._broadcastHandler);
|
|
299
|
+
});
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
disposeUiBehaviors(ui) {
|
|
303
|
+
pacemCore.CustomElementUtils.deleteAttachedPropertyValue(ui, SERIES_DATAITEM);
|
|
304
|
+
pacemCore.CustomElementUtils.deleteAttachedPropertyValue(ui, SERIES_SERIES);
|
|
305
|
+
MANAGED_EVENTS.forEach(type => {
|
|
306
|
+
ui.removeEventListener(type, this._broadcastHandler);
|
|
307
|
+
});
|
|
308
|
+
}
|
|
309
|
+
ensureChartContainer() {
|
|
310
|
+
if (pacemCore.Utils.isNull(this._div)) {
|
|
311
|
+
let div = this._div = this.target || document.createElement('div');
|
|
312
|
+
div.classList.add(pacemCore.PCSS + '-chart-area');
|
|
313
|
+
if (div != this.target) {
|
|
314
|
+
this.parentElement.insertBefore(div, this);
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
return this._div;
|
|
318
|
+
}
|
|
319
|
+
ensureChartBody(type, w, h) {
|
|
320
|
+
if (pacemCore.Utils.isNull(this._body)) {
|
|
321
|
+
let svg = this._body = document.createElementNS(SVG_NS$2, 'svg');
|
|
322
|
+
svg.setAttribute('pacem', '');
|
|
323
|
+
svg.setAttribute('class', pacemCore.PCSS + '-' + type + '-chart');
|
|
324
|
+
svg.setAttribute('preserveAspectRatio', 'xMinYMax slice');
|
|
325
|
+
const g = this._grid = document.createElementNS(SVG_NS$2, 'svg');
|
|
326
|
+
g.setAttribute('pacem', '');
|
|
327
|
+
g.setAttribute('class', 'chart-grid');
|
|
328
|
+
// put mask on series
|
|
329
|
+
let defs = document.createElementNS(SVG_NS$2, 'defs');
|
|
330
|
+
let mask = this._mask = document.createElementNS(SVG_NS$2, 'mask');
|
|
331
|
+
// black (hides)
|
|
332
|
+
let rect = document.createElementNS(SVG_NS$2, 'rect');
|
|
333
|
+
rect.setAttribute('x', '0');
|
|
334
|
+
rect.setAttribute('y', '0');
|
|
335
|
+
rect.setAttribute('width', '100%');
|
|
336
|
+
rect.setAttribute('height', '100%');
|
|
337
|
+
// white (shows)
|
|
338
|
+
let rect0 = document.createElementNS(SVG_NS$2, 'rect');
|
|
339
|
+
rect0.setAttribute('y', '0');
|
|
340
|
+
rect0.setAttribute('width', '100%');
|
|
341
|
+
rect0.setAttribute('fill', '#fff');
|
|
342
|
+
mask.id = 'gnrc_mask_' + this._key;
|
|
343
|
+
mask.appendChild(rect);
|
|
344
|
+
mask.appendChild(rect0);
|
|
345
|
+
// place mask on series so that lines won't be rendered outside the grid borders
|
|
346
|
+
//g2.setAttribute('mask', 'url(#' + mask.id + ')');
|
|
347
|
+
defs.appendChild(mask);
|
|
348
|
+
svg.appendChild(defs);
|
|
349
|
+
svg.appendChild(g);
|
|
350
|
+
this._div.appendChild(svg);
|
|
351
|
+
}
|
|
352
|
+
return this._body;
|
|
353
|
+
}
|
|
354
|
+
chartDataItemToPoint(input, source) {
|
|
355
|
+
switch (this.xAxisType || 'string') {
|
|
356
|
+
case 'number':
|
|
357
|
+
return { x: parseFloat(input.label), y: input.value };
|
|
358
|
+
case 'date':
|
|
359
|
+
return { x: pacemCore.Utils.parseDate(input.label).valueOf(), y: input.value };
|
|
360
|
+
case 'string':
|
|
361
|
+
return { x: source.findIndex(e => e.label == input.label), y: input.value };
|
|
362
|
+
default:
|
|
363
|
+
throw 'Not supported.';
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
getVirtualGrid(items, minX, maxX, minY, maxY, xAxisType = this.xAxisType, steps, labels) {
|
|
367
|
+
const rangeY = getRoundedBoundaries(minY, maxY, steps), stepY = rangeY.round;
|
|
368
|
+
// fill y-axis array
|
|
369
|
+
var y = [];
|
|
370
|
+
for (let j = rangeY.min; j <= rangeY.max; j += stepY) {
|
|
371
|
+
y.push(j.roundoff());
|
|
372
|
+
}
|
|
373
|
+
// fill x-axis array;
|
|
374
|
+
let lang = pacemCore.Utils.lang(this);
|
|
375
|
+
let format = null;
|
|
376
|
+
const intl = this.xAxisFormat;
|
|
377
|
+
if (!pacemCore.Utils.isNullOrEmpty(intl)) {
|
|
378
|
+
switch (xAxisType) {
|
|
379
|
+
case 'number':
|
|
380
|
+
format = (n) => Intl.NumberFormat(lang, intl).format(n);
|
|
381
|
+
break;
|
|
382
|
+
case 'date':
|
|
383
|
+
format = (n) => Intl.DateTimeFormat(lang, intl).format(pacemCore.Utils.parseDate(n));
|
|
384
|
+
break;
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
const x = getEvenlySpaced(items, xAxisType, minX, maxX, format ?? lang, labels);
|
|
388
|
+
return { x, y };
|
|
389
|
+
}
|
|
390
|
+
wipeOut(series = this._series, startIndex = 0) {
|
|
391
|
+
for (var j = series.length - 1; j >= startIndex; j--) {
|
|
392
|
+
series[j].remove();
|
|
393
|
+
}
|
|
394
|
+
series.splice(startIndex);
|
|
395
|
+
}
|
|
396
|
+
buildLinearGradient(seriesIndex, invert = false) {
|
|
397
|
+
const grad = document.createElementNS(SVG_NS$2, 'linearGradient');
|
|
398
|
+
grad.id = this.chartKey + '_grad' + seriesIndex + (invert ? '_inverted' : '');
|
|
399
|
+
if (invert) {
|
|
400
|
+
pacemCore.Utils.addClass(grad, 'bottom-up');
|
|
401
|
+
}
|
|
402
|
+
grad.setAttribute('x1', '0%');
|
|
403
|
+
grad.setAttribute('x2', '0%');
|
|
404
|
+
grad.setAttribute('y1', '0%');
|
|
405
|
+
grad.setAttribute('y2', '100%');
|
|
406
|
+
grad.setAttribute('spreadMethod', 'pad');
|
|
407
|
+
const stop1 = document.createElementNS(SVG_NS$2, 'stop');
|
|
408
|
+
stop1.setAttribute('offset', '0%');
|
|
409
|
+
const stop2 = document.createElementNS(SVG_NS$2, 'stop');
|
|
410
|
+
stop2.setAttribute('offset', '100%');
|
|
411
|
+
grad.appendChild(stop1);
|
|
412
|
+
grad.appendChild(stop2);
|
|
413
|
+
return grad;
|
|
414
|
+
}
|
|
415
|
+
setGradientColor(grad, color) {
|
|
416
|
+
for (let j = 0; j < grad.children.length; j++) {
|
|
417
|
+
const stop = grad.children.item(j);
|
|
418
|
+
if (stop instanceof SVGStopElement) {
|
|
419
|
+
stop.style.stopColor = color;
|
|
420
|
+
}
|
|
421
|
+
}
|
|
422
|
+
}
|
|
423
|
+
estimateYAxisLabelWidth(max) {
|
|
424
|
+
const txt = this.formatYAxisLabel(max);
|
|
425
|
+
return this.estimateLabelWidth(txt);
|
|
426
|
+
}
|
|
427
|
+
estimateLabelWidth(txt) {
|
|
428
|
+
const grid = this._grid;
|
|
429
|
+
if (pacemCore.Utils.isNull(grid)) {
|
|
430
|
+
throw new Error('Unable to estimate label without an underlying grid available.');
|
|
431
|
+
}
|
|
432
|
+
const lbl = document.createElementNS(SVG_NS$2, 'text');
|
|
433
|
+
lbl.textContent = txt;
|
|
434
|
+
grid.appendChild(lbl);
|
|
435
|
+
const size = pacemCore.Utils.offset(lbl);
|
|
436
|
+
lbl.remove();
|
|
437
|
+
return size.width;
|
|
438
|
+
}
|
|
439
|
+
formatYAxisLabel(y) {
|
|
440
|
+
const intl = this.yAxisFormat;
|
|
441
|
+
return pacemCore.Utils.isNullOrEmpty(intl) ? y.toString() : Intl.NumberFormat(pacemCore.Utils.lang(this), intl).format(y);
|
|
442
|
+
}
|
|
443
|
+
estimateXAxisLabelWidth(max) {
|
|
444
|
+
const txt = this.formatXAxisLabel(max);
|
|
445
|
+
return this.estimateLabelWidth(txt);
|
|
446
|
+
}
|
|
447
|
+
formatXAxisLabel(x) {
|
|
448
|
+
const intl = this.xAxisFormat, lang = pacemCore.Utils.lang(this);
|
|
449
|
+
switch (this.xAxisType) {
|
|
450
|
+
case 'date':
|
|
451
|
+
const date = pacemCore.Utils.Dates.parse(x);
|
|
452
|
+
return pacemCore.Utils.isNullOrEmpty(intl) ? date.toLocaleString(lang) : Intl.DateTimeFormat(lang, intl).format(date);
|
|
453
|
+
case 'number':
|
|
454
|
+
const num = x;
|
|
455
|
+
return pacemCore.Utils.isNullOrEmpty(intl) ? num.toLocaleString(lang) : Intl.NumberFormat(lang, intl).format(num);
|
|
456
|
+
default:
|
|
457
|
+
return x;
|
|
458
|
+
}
|
|
459
|
+
}
|
|
460
|
+
draw() {
|
|
461
|
+
this.drawSeries(this._datasource);
|
|
462
|
+
}
|
|
463
|
+
/**
|
|
464
|
+
* Default implementation.
|
|
465
|
+
* @param element
|
|
466
|
+
*/
|
|
467
|
+
getAnchorPoint(element) {
|
|
468
|
+
const rect = pacemCore.Utils.offset(element);
|
|
469
|
+
return { x: rect.left + rect.width * .5, y: rect.top + rect.height * .5 };
|
|
470
|
+
}
|
|
471
|
+
}
|
|
472
|
+
__decorate$3([
|
|
473
|
+
pacemCore.Watch({ converter: pacemCore.PropertyConverters.Element })
|
|
474
|
+
], PacemSeriesChartElement.prototype, "target", void 0);
|
|
475
|
+
__decorate$3([
|
|
476
|
+
pacemCore.Watch({ emit: false, converter: pacemCore.PropertyConverters.String })
|
|
477
|
+
], PacemSeriesChartElement.prototype, "xAxisType", void 0);
|
|
478
|
+
__decorate$3([
|
|
479
|
+
pacemCore.Watch({ emit: false, converter: pacemCore.PropertyConverters.String })
|
|
480
|
+
], PacemSeriesChartElement.prototype, "xAxisPosition", void 0);
|
|
481
|
+
__decorate$3([
|
|
482
|
+
pacemCore.Watch({ emit: false, converter: pacemCore.PropertyConverters.Json })
|
|
483
|
+
], PacemSeriesChartElement.prototype, "datasource", void 0);
|
|
484
|
+
__decorate$3([
|
|
485
|
+
pacemCore.Watch({ emit: false, converter: pacemCore.PropertyConverters.Number })
|
|
486
|
+
], PacemSeriesChartElement.prototype, "yAxisDensity", void 0);
|
|
487
|
+
__decorate$3([
|
|
488
|
+
pacemCore.Watch({ emit: false, converter: pacemCore.PropertyConverters.Number })
|
|
489
|
+
], PacemSeriesChartElement.prototype, "xAxisDensity", void 0);
|
|
490
|
+
__decorate$3([
|
|
491
|
+
pacemCore.Watch({ emit: false, converter: pacemCore.PropertyConverters.Number })
|
|
492
|
+
], PacemSeriesChartElement.prototype, "yAxisMin", void 0);
|
|
493
|
+
__decorate$3([
|
|
494
|
+
pacemCore.Watch({ emit: false, converter: pacemCore.PropertyConverters.Number })
|
|
495
|
+
], PacemSeriesChartElement.prototype, "yAxisMax", void 0);
|
|
496
|
+
__decorate$3([
|
|
497
|
+
pacemCore.Watch({ emit: false, converter: pacemCore.PropertyConverters.Json })
|
|
498
|
+
], PacemSeriesChartElement.prototype, "yAxisFormat", void 0);
|
|
499
|
+
__decorate$3([
|
|
500
|
+
pacemCore.Watch({ emit: false, converter: pacemCore.PropertyConverters.Json })
|
|
501
|
+
], PacemSeriesChartElement.prototype, "xAxisFormat", void 0);
|
|
502
|
+
__decorate$3([
|
|
503
|
+
pacemCore.Throttle(true)
|
|
504
|
+
], PacemSeriesChartElement.prototype, "draw", null);
|
|
505
|
+
|
|
506
|
+
var __decorate$2 = (undefined && undefined.__decorate) || function (decorators, target, key, desc) {
|
|
507
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
508
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
509
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
510
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
511
|
+
};
|
|
512
|
+
//namespace Pacem.Components.Charts {
|
|
513
|
+
/**
|
|
514
|
+
* Returns the anchor points for a Bézier curve.
|
|
515
|
+
* @param p The actual point.
|
|
516
|
+
* @param p0 The previous point, if any.
|
|
517
|
+
* @param p1 The next point, if any.
|
|
518
|
+
*/
|
|
519
|
+
function getSplineCtrlPoints(p, p0, p1) {
|
|
520
|
+
let c0 = p, c1 = p;
|
|
521
|
+
const p0Null = pacemCore.Utils.isNull(p0), p1Null = pacemCore.Utils.isNull(p1);
|
|
522
|
+
if (!(p0Null && p1Null)) {
|
|
523
|
+
const portion = 3;
|
|
524
|
+
let m0, m1;
|
|
525
|
+
if (!p0Null) {
|
|
526
|
+
m0 = (p.y - p0.y) / (p.x - p0.x);
|
|
527
|
+
}
|
|
528
|
+
if (!p1Null) {
|
|
529
|
+
m1 = (p1.y - p.y) / (p1.x - p.x);
|
|
530
|
+
}
|
|
531
|
+
// average slope
|
|
532
|
+
const m = ((m1 || (m0 || 0)) + (m0 || (m1 || 0))) / 2;
|
|
533
|
+
const dx0 = p0Null ? 0 : (p.x - p0.x) / portion;
|
|
534
|
+
const dx1 = p1Null ? 0 : (p1.x - p.x) / portion;
|
|
535
|
+
c0 = { x: p.x - dx0, y: p.y - dx0 * m };
|
|
536
|
+
c1 = { x: p.x + dx1, y: p.y + dx1 * m };
|
|
537
|
+
}
|
|
538
|
+
return { c0: c0, c1: c1 };
|
|
539
|
+
}
|
|
540
|
+
const GET_VAL = pacemCore.CustomElementUtils.getAttachedPropertyValue;
|
|
541
|
+
const SET_VAL = pacemCore.CustomElementUtils.setAttachedPropertyValue;
|
|
542
|
+
pacemCore.CustomElementUtils.deleteAttachedPropertyValue;
|
|
543
|
+
const PADDING_PIXELS$1 = 24;
|
|
544
|
+
const SERIES_MAGNITUDE = 'pacem:chart-series:area';
|
|
545
|
+
const SVG_NS$1 = "http://www.w3.org/2000/svg";
|
|
546
|
+
let PacemChartElement = class PacemChartElement extends PacemSeriesChartElement {
|
|
547
|
+
constructor() {
|
|
548
|
+
super(...arguments);
|
|
549
|
+
this.#hover = false;
|
|
550
|
+
this._enterHandler = (evt) => {
|
|
551
|
+
this.#hover = true;
|
|
552
|
+
};
|
|
553
|
+
this._leaveHandler = (evt) => {
|
|
554
|
+
this.#hover = false;
|
|
555
|
+
};
|
|
556
|
+
this._moveHandler = (evt) => {
|
|
557
|
+
if (!this.#hover) {
|
|
558
|
+
return;
|
|
559
|
+
}
|
|
560
|
+
};
|
|
561
|
+
this._chartFillSeries = [];
|
|
562
|
+
}
|
|
563
|
+
// TODO: implement
|
|
564
|
+
// @Watch({ converter: PropertyConverters.String }) hoverMode: 'abscissa' | 'point'; // 'point' assumed as default
|
|
565
|
+
_getVirtualGrid(items, minX, maxX, minY, maxY, xAxisType = this.xAxisType, steps, labels) {
|
|
566
|
+
return super.getVirtualGrid(items, minX, maxX, minY, maxY, xAxisType, steps, labels);
|
|
567
|
+
}
|
|
568
|
+
_wipe(series = this.chartSeries, startIndex = 0) {
|
|
569
|
+
super.wipeOut(series, startIndex);
|
|
570
|
+
}
|
|
571
|
+
viewActivatedCallback() {
|
|
572
|
+
super.viewActivatedCallback();
|
|
573
|
+
this._setupBehavior();
|
|
574
|
+
}
|
|
575
|
+
disconnectedCallback() {
|
|
576
|
+
this._dismantleBehavior();
|
|
577
|
+
super.disconnectedCallback();
|
|
578
|
+
}
|
|
579
|
+
propertyChangedCallback(name, old, val, first) {
|
|
580
|
+
super.propertyChangedCallback(name, old, val, first);
|
|
581
|
+
switch (name) {
|
|
582
|
+
case 'type':
|
|
583
|
+
case 'aspectRatio':
|
|
584
|
+
this.draw();
|
|
585
|
+
break;
|
|
586
|
+
}
|
|
587
|
+
}
|
|
588
|
+
_setupBehavior() {
|
|
589
|
+
this.addEventListener('mouseenter', this._enterHandler, false);
|
|
590
|
+
this.addEventListener('mouseleave', this._enterHandler, false);
|
|
591
|
+
this.addEventListener('mousemove', this._moveHandler, false);
|
|
592
|
+
}
|
|
593
|
+
_dismantleBehavior() {
|
|
594
|
+
this.removeEventListener('mouseenter', this._enterHandler, false);
|
|
595
|
+
this.removeEventListener('mouseleave', this._leaveHandler, false);
|
|
596
|
+
this.removeEventListener('mousemove', this._moveHandler, false);
|
|
597
|
+
}
|
|
598
|
+
#hover;
|
|
599
|
+
_buildLinearGradient(seriesIndex) {
|
|
600
|
+
return super.buildLinearGradient(seriesIndex);
|
|
601
|
+
}
|
|
602
|
+
_setGradientColor(grad, color) {
|
|
603
|
+
super.setGradientColor(grad, color);
|
|
604
|
+
}
|
|
605
|
+
drawSeries(datasource) {
|
|
606
|
+
if (!this.isReady || pacemCore.Utils.isNull(this.chartSize)) {
|
|
607
|
+
return;
|
|
608
|
+
}
|
|
609
|
+
this.ensureChartContainer();
|
|
610
|
+
const type = this.type || 'line';
|
|
611
|
+
const padding = PADDING_PIXELS$1;
|
|
612
|
+
// resize all
|
|
613
|
+
var size = this.chartSize;
|
|
614
|
+
if (size.height <= padding || size.width <= padding) {
|
|
615
|
+
return;
|
|
616
|
+
}
|
|
617
|
+
// items && labels?
|
|
618
|
+
if (pacemCore.Utils.isNullOrEmpty(datasource) || datasource.every(i => pacemCore.Utils.isNullOrEmpty(i.values))) {
|
|
619
|
+
this._wipe();
|
|
620
|
+
return;
|
|
621
|
+
}
|
|
622
|
+
const body = this.ensureChartBody('line', size.width, size.height);
|
|
623
|
+
// from now on, drawing...
|
|
624
|
+
this.log(pacemCore.Logging.LogLevel.Debug, `Drawing ${type} chart.`);
|
|
625
|
+
// #region computations
|
|
626
|
+
const xAxisType = this.xAxisType || 'string';
|
|
627
|
+
let minY = this.yAxisMin ?? Number.NaN, maxY = this.yAxisMax ?? Number.NaN, minX = Number.NaN, maxX = Number.NaN;
|
|
628
|
+
let stretch = 1;
|
|
629
|
+
// individuate the min/max x & y in order correctly apply aspect ratio...
|
|
630
|
+
for (let series of datasource) {
|
|
631
|
+
let data = series.values;
|
|
632
|
+
if (data && data.length) {
|
|
633
|
+
let j = 0;
|
|
634
|
+
for (let item of data) {
|
|
635
|
+
let pt = this.chartDataItemToPoint(item, data);
|
|
636
|
+
if (pacemCore.Utils.isNull(this.yAxisMin)) {
|
|
637
|
+
minY = isNaN(minY) ? pt.y : Math.min(minY, pt.y);
|
|
638
|
+
}
|
|
639
|
+
if (pacemCore.Utils.isNull(this.yAxisMax)) {
|
|
640
|
+
maxY = isNaN(maxY) ? pt.y : Math.max(maxY, pt.y);
|
|
641
|
+
}
|
|
642
|
+
if (j === 0)
|
|
643
|
+
minX = isNaN(minX) ? pt.x : Math.min(minX, pt.x);
|
|
644
|
+
else if (j === data.length - 1)
|
|
645
|
+
maxX = isNaN(maxX) ? pt.x : Math.max(maxX, pt.x);
|
|
646
|
+
j++;
|
|
647
|
+
}
|
|
648
|
+
}
|
|
649
|
+
}
|
|
650
|
+
/** virtual grid made of x- and y-axis labels */
|
|
651
|
+
const withValues = datasource.find(i => !pacemCore.Utils.isNullOrEmpty(i.values));
|
|
652
|
+
const grid = this._getVirtualGrid(withValues.values, minX, maxX, minY, maxY, xAxisType, this.yAxisDensity, this.xAxisDensity);
|
|
653
|
+
const topGrid = grid.y[grid.y.length - 1], bottomGrid = grid.y[0];
|
|
654
|
+
if (topGrid === bottomGrid)
|
|
655
|
+
return;
|
|
656
|
+
// estimate y-axis max width
|
|
657
|
+
const halfPad = padding * .5;
|
|
658
|
+
const maxYLabelLength = grid.y.reduce((max, i) => Math.max(max, this.estimateYAxisLabelWidth(i) + halfPad), padding);
|
|
659
|
+
const maxXLabelHalfLength = grid.x.reduce((max, i) => Math.max(max, this.estimateXAxisLabelWidth(i)), padding) * .5;
|
|
660
|
+
const paddingYAxis = Math.max(maxXLabelHalfLength, maxYLabelLength), paddingYAxisEnd = maxXLabelHalfLength; // Math.max(padding, this.estimateXAxisLabelWidth(minX) * .5 + PADDING_PIXELS);
|
|
661
|
+
/** width of the WHOLE series dedicated area */
|
|
662
|
+
const seriesWidth = size.width - paddingYAxis - paddingYAxisEnd;
|
|
663
|
+
/** height of the WHOLE series dedicated area */
|
|
664
|
+
const gridHeight = (size.height - 2 * padding);
|
|
665
|
+
const seriesHeight = gridHeight * (1 - (topGrid - bottomGrid - (maxY - minY)) / (topGrid - bottomGrid));
|
|
666
|
+
const seriesY = gridHeight * (topGrid - maxY) / (topGrid - bottomGrid);
|
|
667
|
+
// const seriesY2 = gridHeight * (minY - bottomGrid) / (topGrid - bottomGrid);
|
|
668
|
+
// if not a `graph`
|
|
669
|
+
if (xAxisType !== 'number' || (this.aspectRatio !== 'monometric' && this.aspectRatio !== 'logaritmic')) {
|
|
670
|
+
// consider padding
|
|
671
|
+
stretch = seriesWidth / seriesHeight;
|
|
672
|
+
body.setAttribute('height', size.height.toString());
|
|
673
|
+
body.setAttribute('width', size.width.toString());
|
|
674
|
+
}
|
|
675
|
+
body.setAttribute('viewBox', `0 0 ${size.width} ${size.height}`);
|
|
676
|
+
// #endregion
|
|
677
|
+
// #region render series
|
|
678
|
+
let iter = 0;
|
|
679
|
+
// normalize
|
|
680
|
+
const spanX = maxX - minX, spanY = maxY - minY;
|
|
681
|
+
const normX = 100 * stretch / spanX, normY = 100 / spanY;
|
|
682
|
+
const normPadding = 100 * padding / seriesHeight;
|
|
683
|
+
const buildPoint = (it, series) => {
|
|
684
|
+
let p = this.chartDataItemToPoint(it, series);
|
|
685
|
+
//pt.x *= normX;
|
|
686
|
+
p.y *= normY;
|
|
687
|
+
// Limit as much as possible the magnitude of a number inclued in the svg.
|
|
688
|
+
// Big numbers "overflow the renderer".
|
|
689
|
+
p.x = (p.x - minX) * normX;
|
|
690
|
+
//pt.y = (pt.y - minY) * normY;
|
|
691
|
+
return p;
|
|
692
|
+
};
|
|
693
|
+
const chartSeries = this.chartSeries;
|
|
694
|
+
const chartFillSeries = this._chartFillSeries;
|
|
695
|
+
const chartGrid = this.chartGrid;
|
|
696
|
+
const splineHere = type === 'spline' || type === 'splinearea';
|
|
697
|
+
for (let series of datasource) {
|
|
698
|
+
// does the series svg exist?
|
|
699
|
+
let svg, svgFill;
|
|
700
|
+
let grad;
|
|
701
|
+
if (chartSeries.length > iter) {
|
|
702
|
+
svg = chartSeries[iter];
|
|
703
|
+
svgFill = chartFillSeries[iter];
|
|
704
|
+
grad = svgFill.firstElementChild.firstElementChild;
|
|
705
|
+
}
|
|
706
|
+
else {
|
|
707
|
+
svg = document.createElementNS(SVG_NS$1, 'svg');
|
|
708
|
+
svg.setAttribute('pacem', '');
|
|
709
|
+
body.appendChild(svg);
|
|
710
|
+
svgFill = document.createElementNS(SVG_NS$1, 'svg');
|
|
711
|
+
svgFill.setAttribute('pacem', '');
|
|
712
|
+
body.insertBefore(svgFill, body.children.item(iter + /* <defs> should remain the very first child and 'grid' the very second */ 2));
|
|
713
|
+
// 1st child - defs (gradient)
|
|
714
|
+
const defs = document.createElementNS(SVG_NS$1, 'defs');
|
|
715
|
+
defs.appendChild(grad = this._buildLinearGradient(iter));
|
|
716
|
+
svgFill.appendChild(defs);
|
|
717
|
+
// 2nd child - fill path
|
|
718
|
+
svgFill.appendChild(document.createElementNS(SVG_NS$1, 'path'));
|
|
719
|
+
chartFillSeries.push(svgFill);
|
|
720
|
+
// 1st child stroke path
|
|
721
|
+
svg.appendChild(document.createElementNS(SVG_NS$1, 'path'));
|
|
722
|
+
// 2nd + nth+1 child will be point circles
|
|
723
|
+
chartSeries.push(svg);
|
|
724
|
+
}
|
|
725
|
+
// series positioning:
|
|
726
|
+
/*
|
|
727
|
+
--------------------------------------
|
|
728
|
+
| |--------------------------------|
|
|
729
|
+
|pad| series here |
|
|
730
|
+
| |--------------------------------|
|
|
731
|
+
--------------------------------------
|
|
732
|
+
| | pad |
|
|
733
|
+
--------------------------------------
|
|
734
|
+
*/
|
|
735
|
+
let className = 'chart-series';
|
|
736
|
+
const fill = type === 'area' || type === 'splinearea';
|
|
737
|
+
if (!pacemCore.Utils.isNullOrEmpty(series.className)) {
|
|
738
|
+
className += ' ' + series.className;
|
|
739
|
+
}
|
|
740
|
+
[svg, svgFill].forEach(s => {
|
|
741
|
+
s.setAttribute('class', className);
|
|
742
|
+
s.setAttribute('x', paddingYAxis.toString());
|
|
743
|
+
s.setAttribute('y', seriesY.toString());
|
|
744
|
+
s.setAttribute('width', seriesWidth.toString());
|
|
745
|
+
s.setAttribute('height', (seriesHeight + 2 * padding).toString());
|
|
746
|
+
});
|
|
747
|
+
pacemCore.Utils.addClass(svgFill, pacemCore.PCSS + '-inert');
|
|
748
|
+
// pick path as single child element for the series.
|
|
749
|
+
let pathFill = svgFill.children.item(1);
|
|
750
|
+
let path = svg.firstElementChild;
|
|
751
|
+
path.style.stroke = series.color;
|
|
752
|
+
pathFill.style.stroke =
|
|
753
|
+
path.style.fill = 'none';
|
|
754
|
+
this._setGradientColor(grad, series.color);
|
|
755
|
+
pathFill.style.fill = `url(#${grad.id})`;
|
|
756
|
+
pathFill.style.display = fill ? '' : 'none';
|
|
757
|
+
let d = '';
|
|
758
|
+
let data = series.values;
|
|
759
|
+
if (data && data.length) {
|
|
760
|
+
const addInteractivePoint = (center, j, series, dataItem, color) => {
|
|
761
|
+
const ndx = j + /* count the <path> child */ 1;
|
|
762
|
+
let circle;
|
|
763
|
+
if (svg.children.length > ndx) {
|
|
764
|
+
circle = svg.children.item(ndx);
|
|
765
|
+
}
|
|
766
|
+
else {
|
|
767
|
+
circle = document.createElementNS(SVG_NS$1, 'line');
|
|
768
|
+
circle.setAttribute('class', 'circle');
|
|
769
|
+
svg.appendChild(circle);
|
|
770
|
+
}
|
|
771
|
+
const x1 = center.x.toString(), y1 = (-center.y).toString();
|
|
772
|
+
circle.setAttribute('x1', x1);
|
|
773
|
+
circle.setAttribute('y1', y1);
|
|
774
|
+
circle.setAttribute('x2', x1);
|
|
775
|
+
circle.setAttribute('y2', y1);
|
|
776
|
+
circle.style.stroke = color;
|
|
777
|
+
this.assignUiBehaviors(circle, series, dataItem, color);
|
|
778
|
+
};
|
|
779
|
+
const wipeExceedingPoints = () => {
|
|
780
|
+
const remaining = series.datapoints ? (data.length + 1) : 1;
|
|
781
|
+
for (let k = svg.children.length - 1; k >= remaining; k--) {
|
|
782
|
+
var circle = svg.children.item(k);
|
|
783
|
+
this.disposeUiBehaviors(circle);
|
|
784
|
+
circle.remove();
|
|
785
|
+
}
|
|
786
|
+
};
|
|
787
|
+
if (splineHere) {
|
|
788
|
+
// #region SPLINE
|
|
789
|
+
let pt0, pt, c0;
|
|
790
|
+
for (let j = 0; j < data.length; j++) {
|
|
791
|
+
const item = data[j];
|
|
792
|
+
if (isNaN(item.value)) {
|
|
793
|
+
continue;
|
|
794
|
+
}
|
|
795
|
+
if (!pt) {
|
|
796
|
+
pt = buildPoint(item, data);
|
|
797
|
+
}
|
|
798
|
+
let pt1;
|
|
799
|
+
if (splineHere && j < (data.length - 1)) {
|
|
800
|
+
pt1 = buildPoint(data[j + 1], data);
|
|
801
|
+
}
|
|
802
|
+
// accumulate `area` for sorting
|
|
803
|
+
let areaSoFar = GET_VAL(series, SERIES_MAGNITUDE, 0);
|
|
804
|
+
areaSoFar += item.value;
|
|
805
|
+
SET_VAL(series, SERIES_MAGNITUDE, areaSoFar);
|
|
806
|
+
var c = getSplineCtrlPoints(pt, pt0, pt1);
|
|
807
|
+
d += pacemCore.Utils.isNullOrEmpty(d) ? `M${pt.x},${-pt.y} ` : `C${c0.x},${-c0.y} ${c.c0.x},${-c.c0.y} ${pt.x},${-pt.y} `;
|
|
808
|
+
if (series.datapoints) {
|
|
809
|
+
addInteractivePoint(pt, j, series, item, series.color);
|
|
810
|
+
}
|
|
811
|
+
// step next
|
|
812
|
+
pt0 = pt;
|
|
813
|
+
pt = pt1, c0 = c.c1;
|
|
814
|
+
}
|
|
815
|
+
// #endregion
|
|
816
|
+
}
|
|
817
|
+
else {
|
|
818
|
+
// #region LINE
|
|
819
|
+
let j = 0;
|
|
820
|
+
for (let item of data) {
|
|
821
|
+
if (isNaN(item.value)) {
|
|
822
|
+
continue;
|
|
823
|
+
}
|
|
824
|
+
const pt = buildPoint(item, data);
|
|
825
|
+
// accumulate `area` for sorting
|
|
826
|
+
let areaSoFar = GET_VAL(series, SERIES_MAGNITUDE, 0);
|
|
827
|
+
areaSoFar += item.value;
|
|
828
|
+
SET_VAL(series, SERIES_MAGNITUDE, areaSoFar);
|
|
829
|
+
d += pacemCore.Utils.isNullOrEmpty(d) ? `M${pt.x},${-pt.y} ` : `L${pt.x},${-pt.y} `;
|
|
830
|
+
if (series.datapoints) {
|
|
831
|
+
addInteractivePoint(pt, j, series, item, series.color);
|
|
832
|
+
}
|
|
833
|
+
j++;
|
|
834
|
+
}
|
|
835
|
+
// #endregion
|
|
836
|
+
}
|
|
837
|
+
wipeExceedingPoints();
|
|
838
|
+
}
|
|
839
|
+
path.setAttribute('d', d);
|
|
840
|
+
pathFill.setAttribute('d', d + `V${(-minY * normY)} H0 Z`);
|
|
841
|
+
// tick
|
|
842
|
+
iter++;
|
|
843
|
+
}
|
|
844
|
+
// remove exceeding series
|
|
845
|
+
this._wipe(chartFillSeries, iter);
|
|
846
|
+
this._wipe(chartSeries, iter);
|
|
847
|
+
const w0 = 100 * stretch, h0 = 100 + 2 * normPadding, x0 = 0, //minX * normX,
|
|
848
|
+
y0 = maxY * normY + normPadding;
|
|
849
|
+
const svbox = `${x0} ${-y0} ${w0} ${h0}`;
|
|
850
|
+
for (var svg of chartSeries.concat(chartFillSeries)) {
|
|
851
|
+
svg.setAttribute('viewBox', svbox);
|
|
852
|
+
}
|
|
853
|
+
const chartMask = this.chartMask;
|
|
854
|
+
let mask = chartMask.children.item(1);
|
|
855
|
+
mask.setAttribute('x', x0.toString());
|
|
856
|
+
mask.setAttribute('height', (size.height - padding).toString());
|
|
857
|
+
// #endregion
|
|
858
|
+
// #region grid
|
|
859
|
+
chartGrid.setAttribute('viewBox', `0 0 ${size.width} ${size.height}`);
|
|
860
|
+
if (grid.x.length <= 1 || grid.y.length <= 1) {
|
|
861
|
+
for (let j = chartGrid.children.length - 1; j >= 0; j--) {
|
|
862
|
+
chartGrid.children.item(j).remove();
|
|
863
|
+
}
|
|
864
|
+
return;
|
|
865
|
+
}
|
|
866
|
+
let pgrid;
|
|
867
|
+
if (chartGrid.children.length > 0) {
|
|
868
|
+
pgrid = chartGrid.children.item(0);
|
|
869
|
+
}
|
|
870
|
+
else {
|
|
871
|
+
pgrid = document.createElementNS(SVG_NS$1, 'path');
|
|
872
|
+
chartGrid.appendChild(pgrid);
|
|
873
|
+
}
|
|
874
|
+
const tick = padding * .25;
|
|
875
|
+
let lblCounter = 0;
|
|
876
|
+
let ensureLabel = (index, x, y, txt) => {
|
|
877
|
+
const ndx = index + /* <path> is the first child element */ 1;
|
|
878
|
+
let lbl;
|
|
879
|
+
if (chartGrid.children.length <= ndx) {
|
|
880
|
+
lbl = document.createElementNS(SVG_NS$1, 'text');
|
|
881
|
+
chartGrid.appendChild(lbl);
|
|
882
|
+
}
|
|
883
|
+
else {
|
|
884
|
+
lbl = chartGrid.children.item(ndx);
|
|
885
|
+
}
|
|
886
|
+
lbl.textContent = txt;
|
|
887
|
+
lbl.setAttribute('x', x.toString());
|
|
888
|
+
lbl.setAttribute('y', y.toString());
|
|
889
|
+
return lbl;
|
|
890
|
+
};
|
|
891
|
+
let dgrid = `M${paddingYAxis},${padding} v${gridHeight}`; //H${w}
|
|
892
|
+
// x
|
|
893
|
+
let j = 0;
|
|
894
|
+
const xincr = seriesWidth / (grid.x.length - 1), yincr = gridHeight / (grid.y.length - 1);
|
|
895
|
+
if (this.xAxisPosition !== 'none') {
|
|
896
|
+
for (var x of grid.x) {
|
|
897
|
+
const xcoord = paddingYAxis + j * xincr;
|
|
898
|
+
if (this.xAxisPosition === 'top') {
|
|
899
|
+
dgrid += ` M${xcoord},${padding} v${-tick}`;
|
|
900
|
+
let lbl = ensureLabel(lblCounter++, xcoord, 0, x);
|
|
901
|
+
lbl.setAttribute('text-anchor', 'middle');
|
|
902
|
+
lbl.setAttribute('alignment-baseline', 'hanging');
|
|
903
|
+
}
|
|
904
|
+
else {
|
|
905
|
+
let ycoord = gridHeight + padding;
|
|
906
|
+
dgrid += ` M${xcoord},${ycoord} v${tick}`;
|
|
907
|
+
ensureLabel(lblCounter++, xcoord, ycoord + padding, x).setAttribute('text-anchor', 'middle');
|
|
908
|
+
}
|
|
909
|
+
j++;
|
|
910
|
+
}
|
|
911
|
+
}
|
|
912
|
+
// y
|
|
913
|
+
j = 0;
|
|
914
|
+
for (var y of grid.y) {
|
|
915
|
+
const ycoord = gridHeight + padding - j * yincr, xcoord = paddingYAxis - tick;
|
|
916
|
+
dgrid += ` M${xcoord},${ycoord} H${(seriesWidth + paddingYAxis)}`;
|
|
917
|
+
let txt = this.formatYAxisLabel(y);
|
|
918
|
+
ensureLabel(lblCounter++, xcoord - tick, ycoord, txt).setAttribute('text-anchor', 'end');
|
|
919
|
+
j++;
|
|
920
|
+
}
|
|
921
|
+
pgrid.setAttribute('d', dgrid);
|
|
922
|
+
// exceeding labels?
|
|
923
|
+
for (let j = chartGrid.children.length - 1; j > lblCounter; j--) {
|
|
924
|
+
chartGrid.children.item(j).remove();
|
|
925
|
+
}
|
|
926
|
+
// #endregion
|
|
927
|
+
// #region abscissa cursor
|
|
928
|
+
// #endregion
|
|
929
|
+
}
|
|
930
|
+
};
|
|
931
|
+
__decorate$2([
|
|
932
|
+
pacemCore.Watch({ emit: false, converter: pacemCore.PropertyConverters.String })
|
|
933
|
+
], PacemChartElement.prototype, "type", void 0);
|
|
934
|
+
__decorate$2([
|
|
935
|
+
pacemCore.Watch({ converter: pacemCore.PropertyConverters.String })
|
|
936
|
+
], PacemChartElement.prototype, "aspectRatio", void 0);
|
|
937
|
+
PacemChartElement = __decorate$2([
|
|
938
|
+
pacemCore.CustomElement({ tagName: pacemCore.P + '-chart' })
|
|
939
|
+
], PacemChartElement);
|
|
940
|
+
|
|
941
|
+
var __decorate$1 = (undefined && undefined.__decorate) || function (decorators, target, key, desc) {
|
|
942
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
943
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
944
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
945
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
946
|
+
};
|
|
947
|
+
//namespace Pacem.Components.Charts {
|
|
948
|
+
const SVG_NS = "http://www.w3.org/2000/svg";
|
|
949
|
+
const PADDING_PIXELS = 24;
|
|
950
|
+
let PacemColumnChartElement = class PacemColumnChartElement extends PacemSeriesChartElement {
|
|
951
|
+
constructor() {
|
|
952
|
+
super(...arguments);
|
|
953
|
+
/** Set a value between 0 and 1, for the columns' stack/cluster horizontal size (percentage of the available space). */
|
|
954
|
+
this.groupWidth = .0;
|
|
955
|
+
}
|
|
956
|
+
propertyChangedCallback(name, old, val, first) {
|
|
957
|
+
super.propertyChangedCallback(name, old, val, first);
|
|
958
|
+
if (name === 'type' || name === 'groupWidth') {
|
|
959
|
+
this.draw();
|
|
960
|
+
}
|
|
961
|
+
}
|
|
962
|
+
_safen(w = this.groupWidth) {
|
|
963
|
+
return Math.min(1, Math.max(w || .75, 0));
|
|
964
|
+
}
|
|
965
|
+
drawSeries(datasource) {
|
|
966
|
+
if (!this.isReady || pacemCore.Utils.isNull(this.chartSize)) {
|
|
967
|
+
return;
|
|
968
|
+
}
|
|
969
|
+
this.ensureChartContainer();
|
|
970
|
+
const type = this.type || 'cluster';
|
|
971
|
+
const padding = PADDING_PIXELS;
|
|
972
|
+
// resize all
|
|
973
|
+
var size = this.chartSize;
|
|
974
|
+
if (size.height <= padding || size.width <= padding) {
|
|
975
|
+
return;
|
|
976
|
+
}
|
|
977
|
+
// items && labels?
|
|
978
|
+
if (pacemCore.Utils.isNullOrEmpty(datasource) || datasource.every(i => pacemCore.Utils.isNullOrEmpty(i.values))) {
|
|
979
|
+
this.wipeOut();
|
|
980
|
+
return;
|
|
981
|
+
}
|
|
982
|
+
const body = this.ensureChartBody('column', size.width, size.height);
|
|
983
|
+
// from now on, drawing...
|
|
984
|
+
this.log(pacemCore.Logging.LogLevel.Debug, `Drawing ${type} chart.`);
|
|
985
|
+
// #region computations
|
|
986
|
+
const xAxisType = this.xAxisType || 'string';
|
|
987
|
+
let minY = this.yAxisMin ?? 0, maxY = this.yAxisMax ?? 0, minX = 0, maxX = 0, maxCount = 0;
|
|
988
|
+
let stretch = 1;
|
|
989
|
+
const accumulator = { negative: [], positive: [] };
|
|
990
|
+
// individuate the min/max x & y in order correctly apply aspect ratio...
|
|
991
|
+
for (let series of datasource) {
|
|
992
|
+
let data = series.values;
|
|
993
|
+
if (!pacemCore.Utils.isNullOrEmpty(data)) {
|
|
994
|
+
let j = 0;
|
|
995
|
+
for (let item of data) {
|
|
996
|
+
accumulator.positive[j] = accumulator.positive[j] || 0;
|
|
997
|
+
accumulator.negative[j] = accumulator.negative[j] || 0;
|
|
998
|
+
let pt = this.chartDataItemToPoint(item, data);
|
|
999
|
+
if (pacemCore.Utils.isNull(this.yAxisMin)) {
|
|
1000
|
+
minY = Math.min(minY, pt.y);
|
|
1001
|
+
}
|
|
1002
|
+
if (pacemCore.Utils.isNull(this.yAxisMax)) {
|
|
1003
|
+
maxY = Math.max(maxY, pt.y);
|
|
1004
|
+
}
|
|
1005
|
+
minX = Math.min(minX, pt.x);
|
|
1006
|
+
maxX = Math.max(maxX, pt.x);
|
|
1007
|
+
if (pt.y > 0) {
|
|
1008
|
+
accumulator.positive[j] += pt.y;
|
|
1009
|
+
}
|
|
1010
|
+
else if (pt.y < 0) {
|
|
1011
|
+
accumulator.negative[j] += pt.y;
|
|
1012
|
+
}
|
|
1013
|
+
j++;
|
|
1014
|
+
}
|
|
1015
|
+
maxCount = Math.max(maxCount, data.length);
|
|
1016
|
+
}
|
|
1017
|
+
}
|
|
1018
|
+
const stacked = type === 'stack';
|
|
1019
|
+
if (stacked) {
|
|
1020
|
+
if (pacemCore.Utils.isNull(this.yAxisMin))
|
|
1021
|
+
minY = Math.min.apply(null, accumulator.negative);
|
|
1022
|
+
if (pacemCore.Utils.isNull(this.yAxisMax))
|
|
1023
|
+
maxY = Math.max.apply(null, accumulator.positive);
|
|
1024
|
+
}
|
|
1025
|
+
if (maxX === 0 && maxX === minX) {
|
|
1026
|
+
maxX = 1;
|
|
1027
|
+
}
|
|
1028
|
+
// add "1 slot" (half at the beginning and half after the end)
|
|
1029
|
+
const slot = (maxX - minX) / maxCount, halfSlot = slot * .5,
|
|
1030
|
+
// the following are usefule during the very rendering
|
|
1031
|
+
availSlot = slot * this._safen(this.groupWidth), halfAvailSlot = availSlot * .5;
|
|
1032
|
+
/** virtual grid made of x- and y-axis labels */
|
|
1033
|
+
const withValues = datasource.find(i => !pacemCore.Utils.isNullOrEmpty(i.values));
|
|
1034
|
+
const grid = this.getVirtualGrid(withValues.values, minX - halfSlot, maxX + halfSlot, minY, maxY, xAxisType, this.yAxisDensity, this.xAxisDensity);
|
|
1035
|
+
const topGrid = grid.y[grid.y.length - 1], bottomGrid = grid.y[0];
|
|
1036
|
+
if (topGrid === bottomGrid) {
|
|
1037
|
+
return;
|
|
1038
|
+
}
|
|
1039
|
+
// estimate y-axis max width
|
|
1040
|
+
const halfPad = PADDING_PIXELS * .5;
|
|
1041
|
+
const paddingYAxis = grid.y.reduce((max, i) => Math.max(max, this.estimateYAxisLabelWidth(i) + halfPad), padding);
|
|
1042
|
+
/** width of the WHOLE series dedicated area */
|
|
1043
|
+
const seriesWidth = size.width - paddingYAxis - padding;
|
|
1044
|
+
/** height of the WHOLE series dedicated area */
|
|
1045
|
+
const gridHeight = (size.height - 2 * padding);
|
|
1046
|
+
const seriesHeight = gridHeight * (1 - (topGrid - bottomGrid - (maxY - minY)) / (topGrid - bottomGrid));
|
|
1047
|
+
const seriesY = gridHeight * (topGrid - maxY) / (topGrid - bottomGrid);
|
|
1048
|
+
// consider padding
|
|
1049
|
+
stretch = seriesWidth / seriesHeight;
|
|
1050
|
+
body.setAttribute('height', size.height.toString());
|
|
1051
|
+
body.setAttribute('width', size.width.toString());
|
|
1052
|
+
body.setAttribute('viewBox', `0 0 ${size.width} ${size.height}`);
|
|
1053
|
+
// #endregion
|
|
1054
|
+
// #region render series
|
|
1055
|
+
let iter = 0;
|
|
1056
|
+
// normalize
|
|
1057
|
+
const spanX = maxX - minX, spanY = maxY - minY;
|
|
1058
|
+
const normX = 100 * stretch / spanX, normY = 100 / spanY;
|
|
1059
|
+
const normPadding = 100 * padding / seriesHeight;
|
|
1060
|
+
const chartSeries = this.chartSeries;
|
|
1061
|
+
const chartGrid = this.chartGrid;
|
|
1062
|
+
const xPad = (halfSlot - minX) * normX;
|
|
1063
|
+
const buildPoint = (it, series) => {
|
|
1064
|
+
const p = this.chartDataItemToPoint(it, series);
|
|
1065
|
+
return {
|
|
1066
|
+
x: xPad + p.x * 2 * xPad,
|
|
1067
|
+
y: p.y * normY
|
|
1068
|
+
};
|
|
1069
|
+
};
|
|
1070
|
+
var accumulators = new Array(maxCount);
|
|
1071
|
+
for (let series of datasource) {
|
|
1072
|
+
// does the series svg exist?
|
|
1073
|
+
let svg;
|
|
1074
|
+
let grad;
|
|
1075
|
+
let gradInv;
|
|
1076
|
+
let g;
|
|
1077
|
+
if (chartSeries.length > iter) {
|
|
1078
|
+
svg = chartSeries[iter];
|
|
1079
|
+
grad = svg.firstElementChild.firstElementChild;
|
|
1080
|
+
gradInv = svg.firstElementChild.lastElementChild;
|
|
1081
|
+
g = svg.lastElementChild;
|
|
1082
|
+
}
|
|
1083
|
+
else {
|
|
1084
|
+
svg = document.createElementNS(SVG_NS, 'svg');
|
|
1085
|
+
svg.setAttribute('pacem', '');
|
|
1086
|
+
chartGrid.insertAdjacentElement('afterend', svg);
|
|
1087
|
+
const defs = document.createElementNS(SVG_NS, 'defs');
|
|
1088
|
+
defs.appendChild(grad = this.buildLinearGradient(iter));
|
|
1089
|
+
defs.appendChild(gradInv = this.buildLinearGradient(iter, true));
|
|
1090
|
+
svg.appendChild(defs);
|
|
1091
|
+
svg.appendChild(g = document.createElementNS(SVG_NS, 'g'));
|
|
1092
|
+
chartSeries.push(svg);
|
|
1093
|
+
}
|
|
1094
|
+
// series positioning:
|
|
1095
|
+
/*
|
|
1096
|
+
--------------------------------------
|
|
1097
|
+
| |--------------------------------|
|
|
1098
|
+
|pad| series here |
|
|
1099
|
+
| |--------------------------------|
|
|
1100
|
+
--------------------------------------
|
|
1101
|
+
| | pad |
|
|
1102
|
+
--------------------------------------
|
|
1103
|
+
*/
|
|
1104
|
+
var className = 'chart-series series-fill';
|
|
1105
|
+
if (!pacemCore.Utils.isNullOrEmpty(series.className)) {
|
|
1106
|
+
className += ' ' + series.className;
|
|
1107
|
+
}
|
|
1108
|
+
svg.setAttribute('class', className);
|
|
1109
|
+
svg.setAttribute('x', paddingYAxis.toString());
|
|
1110
|
+
svg.setAttribute('y', seriesY.toString());
|
|
1111
|
+
svg.setAttribute('width', seriesWidth.toString());
|
|
1112
|
+
svg.setAttribute('height', (seriesHeight + 2 * padding).toString());
|
|
1113
|
+
const data = series.values;
|
|
1114
|
+
const spinRect = (index) => {
|
|
1115
|
+
while (index >= g.children.length) {
|
|
1116
|
+
g.appendChild(document.createElementNS(SVG_NS, 'rect'));
|
|
1117
|
+
}
|
|
1118
|
+
const rect = g.children.item(index);
|
|
1119
|
+
rect.style.stroke = series.color;
|
|
1120
|
+
const gradient = data[index].value < 0 ? gradInv : grad;
|
|
1121
|
+
this.setGradientColor(gradient, series.color);
|
|
1122
|
+
rect.style.fill = `url(#${gradient.id})`;
|
|
1123
|
+
return rect;
|
|
1124
|
+
};
|
|
1125
|
+
// fix 0 y
|
|
1126
|
+
const precision = 10;
|
|
1127
|
+
const assignVerticalCoordsCoords = (rect, pt0, pt, pt0Start = pt0) => {
|
|
1128
|
+
const y = -Math.max(pt0.y, pt.y), h = Math.abs(pt.y - pt0.y);
|
|
1129
|
+
const fnAssign = () => {
|
|
1130
|
+
rect.setAttribute('y', y.toFixed(precision));
|
|
1131
|
+
rect.setAttribute('height', h.toFixed(precision));
|
|
1132
|
+
};
|
|
1133
|
+
if (!rect.hasAttribute('y')) {
|
|
1134
|
+
// boostrap easing animated transition
|
|
1135
|
+
rect.setAttribute('y', '-' + pt0Start.y.toFixed(precision));
|
|
1136
|
+
rect.setAttribute('height', '0');
|
|
1137
|
+
requestAnimationFrame(fnAssign);
|
|
1138
|
+
}
|
|
1139
|
+
else {
|
|
1140
|
+
fnAssign();
|
|
1141
|
+
}
|
|
1142
|
+
};
|
|
1143
|
+
if (!pacemCore.Utils.isNullOrEmpty(data)) {
|
|
1144
|
+
const pt00 = buildPoint({ value: 0, label: data[0].label }, data);
|
|
1145
|
+
if (stacked) {
|
|
1146
|
+
// #region STACK
|
|
1147
|
+
const iHalfAvailSlot = halfAvailSlot * normX, iAvailSlotAttr = (availSlot * normX).toFixed(precision);
|
|
1148
|
+
for (let j = 0; j < data.length; j++) {
|
|
1149
|
+
const item = data[j];
|
|
1150
|
+
if (pacemCore.Utils.isNullOrEmpty(accumulators[j])) {
|
|
1151
|
+
accumulators[j] = { positive: 0, negative: 0 };
|
|
1152
|
+
}
|
|
1153
|
+
const accumulator = accumulators[j];
|
|
1154
|
+
let posCursor = accumulator.positive, negCursor = accumulator.negative;
|
|
1155
|
+
const neg = item.value < 0, y = neg ? negCursor : (posCursor + item.value), y0 = neg ? (negCursor + item.value) : posCursor;
|
|
1156
|
+
const pt = buildPoint({ label: item.label, value: y }, data);
|
|
1157
|
+
const pt0 = buildPoint({ label: item.label, value: y0 }, data);
|
|
1158
|
+
const x = pt.x - iHalfAvailSlot;
|
|
1159
|
+
const rect = spinRect(j);
|
|
1160
|
+
this.assignUiBehaviors(rect, series, item);
|
|
1161
|
+
rect.setAttribute('x', x.toFixed(precision));
|
|
1162
|
+
rect.setAttribute('width', iAvailSlotAttr);
|
|
1163
|
+
assignVerticalCoordsCoords(rect, pt0, pt, pt00);
|
|
1164
|
+
if (neg) {
|
|
1165
|
+
accumulator.negative = y0;
|
|
1166
|
+
}
|
|
1167
|
+
else {
|
|
1168
|
+
accumulator.positive = y;
|
|
1169
|
+
}
|
|
1170
|
+
}
|
|
1171
|
+
// #endregion
|
|
1172
|
+
}
|
|
1173
|
+
else {
|
|
1174
|
+
// #region CLUSTER
|
|
1175
|
+
const iHalfAvailSlot = halfAvailSlot * normX, iSlot = (iHalfAvailSlot * 2) / datasource.length, //,
|
|
1176
|
+
iSlotAttr = iSlot.toFixed(precision);
|
|
1177
|
+
for (let j = 0; j < data.length; j++) {
|
|
1178
|
+
// looping through the series-data
|
|
1179
|
+
const item = data[j];
|
|
1180
|
+
const pt = buildPoint(item, data);
|
|
1181
|
+
const x = pt.x - iHalfAvailSlot + iSlot * iter;
|
|
1182
|
+
const rect = spinRect(j);
|
|
1183
|
+
this.assignUiBehaviors(rect, series, item);
|
|
1184
|
+
rect.setAttribute('x', x.toFixed(precision));
|
|
1185
|
+
rect.setAttribute('width', iSlotAttr);
|
|
1186
|
+
assignVerticalCoordsCoords(rect, pt00, pt);
|
|
1187
|
+
}
|
|
1188
|
+
// #endregion
|
|
1189
|
+
}
|
|
1190
|
+
}
|
|
1191
|
+
// cleanup exceeding rects
|
|
1192
|
+
for (let j = g.children.length - 1; j >= data?.length || 0; j--) {
|
|
1193
|
+
const rect = g.children.item(j);
|
|
1194
|
+
this.disposeUiBehaviors(rect);
|
|
1195
|
+
rect.remove();
|
|
1196
|
+
}
|
|
1197
|
+
// tick
|
|
1198
|
+
iter++;
|
|
1199
|
+
}
|
|
1200
|
+
// remove exceeding series
|
|
1201
|
+
this.wipeOut(chartSeries, iter);
|
|
1202
|
+
const w0 = 100 * stretch, h0 = 100 + 2 * normPadding, x0 = 0, //minX * normX,
|
|
1203
|
+
y0 = maxY * normY + normPadding;
|
|
1204
|
+
const svbox = `${x0} ${-y0} ${w0} ${h0}`;
|
|
1205
|
+
for (var svg of chartSeries) {
|
|
1206
|
+
svg.setAttribute('viewBox', svbox);
|
|
1207
|
+
}
|
|
1208
|
+
const chartMask = this.chartMask;
|
|
1209
|
+
let mask = chartMask.children.item(1);
|
|
1210
|
+
mask.setAttribute('x', x0.toString());
|
|
1211
|
+
mask.setAttribute('height', (size.height - padding).toString());
|
|
1212
|
+
// #endregion
|
|
1213
|
+
// #region grid
|
|
1214
|
+
chartGrid.setAttribute('viewBox', `0 0 ${size.width} ${size.height}`);
|
|
1215
|
+
if (grid.x.length < 1 || grid.y.length <= 1) {
|
|
1216
|
+
for (let j = chartGrid.children.length - 1; j >= 0; j--) {
|
|
1217
|
+
chartGrid.children.item(j).remove();
|
|
1218
|
+
}
|
|
1219
|
+
return;
|
|
1220
|
+
}
|
|
1221
|
+
let pgrid;
|
|
1222
|
+
if (chartGrid.children.length > 0) {
|
|
1223
|
+
pgrid = chartGrid.children.item(0);
|
|
1224
|
+
}
|
|
1225
|
+
else {
|
|
1226
|
+
pgrid = document.createElementNS(SVG_NS, 'path');
|
|
1227
|
+
chartGrid.appendChild(pgrid);
|
|
1228
|
+
}
|
|
1229
|
+
const tick = PADDING_PIXELS * .25;
|
|
1230
|
+
let lblCounter = 0;
|
|
1231
|
+
let ensureLabel = (index, x, y, txt) => {
|
|
1232
|
+
const ndx = index + /* <path> is the first child element */ 1;
|
|
1233
|
+
let lbl;
|
|
1234
|
+
if (chartGrid.children.length <= ndx) {
|
|
1235
|
+
lbl = document.createElementNS(SVG_NS, 'text');
|
|
1236
|
+
chartGrid.appendChild(lbl);
|
|
1237
|
+
}
|
|
1238
|
+
else {
|
|
1239
|
+
lbl = chartGrid.children.item(ndx);
|
|
1240
|
+
}
|
|
1241
|
+
lbl.textContent = txt;
|
|
1242
|
+
lbl.setAttribute('x', x.toString());
|
|
1243
|
+
lbl.setAttribute('y', y.toString());
|
|
1244
|
+
return lbl;
|
|
1245
|
+
};
|
|
1246
|
+
let dgrid = `M${paddingYAxis},${padding} v${gridHeight}`; //H${w}
|
|
1247
|
+
// x
|
|
1248
|
+
let j = 0;
|
|
1249
|
+
const xincr = seriesWidth / (grid.x.length /*- 1 consider the extra slot */), yincr = gridHeight / (grid.y.length - 1);
|
|
1250
|
+
if (this.xAxisPosition !== 'none') {
|
|
1251
|
+
const paddingX = xincr * .5 + paddingYAxis;
|
|
1252
|
+
for (var x of grid.x) {
|
|
1253
|
+
let xcoord = paddingX + j * xincr;
|
|
1254
|
+
if (this.xAxisPosition === 'top') {
|
|
1255
|
+
dgrid += ` M${xcoord},${padding} v${-tick}`;
|
|
1256
|
+
let lbl = ensureLabel(lblCounter++, xcoord, 0, x);
|
|
1257
|
+
lbl.setAttribute('text-anchor', 'middle');
|
|
1258
|
+
lbl.setAttribute('alignment-baseline', 'hanging');
|
|
1259
|
+
}
|
|
1260
|
+
else {
|
|
1261
|
+
let ycoord = gridHeight + padding;
|
|
1262
|
+
dgrid += ` M${xcoord},${ycoord} v${tick}`;
|
|
1263
|
+
ensureLabel(lblCounter++, xcoord, ycoord + padding, x).setAttribute('text-anchor', 'middle');
|
|
1264
|
+
}
|
|
1265
|
+
j++;
|
|
1266
|
+
}
|
|
1267
|
+
}
|
|
1268
|
+
// y
|
|
1269
|
+
j = 0;
|
|
1270
|
+
for (var y of grid.y) {
|
|
1271
|
+
const ycoord = gridHeight + padding - j * yincr, xcoord = paddingYAxis - tick;
|
|
1272
|
+
dgrid += ` M${xcoord},${ycoord} H${(seriesWidth + paddingYAxis)}`;
|
|
1273
|
+
let txt = this.formatYAxisLabel(y);
|
|
1274
|
+
ensureLabel(lblCounter++, xcoord - tick, ycoord, txt).setAttribute('text-anchor', 'end');
|
|
1275
|
+
j++;
|
|
1276
|
+
}
|
|
1277
|
+
pgrid.setAttribute('d', dgrid);
|
|
1278
|
+
// exceeding labels?
|
|
1279
|
+
for (let j = chartGrid.children.length - 1; j > lblCounter; j--) {
|
|
1280
|
+
chartGrid.children.item(j).remove();
|
|
1281
|
+
}
|
|
1282
|
+
// #endregion
|
|
1283
|
+
}
|
|
1284
|
+
};
|
|
1285
|
+
__decorate$1([
|
|
1286
|
+
pacemCore.Watch({ converter: pacemCore.PropertyConverters.String })
|
|
1287
|
+
], PacemColumnChartElement.prototype, "type", void 0);
|
|
1288
|
+
__decorate$1([
|
|
1289
|
+
pacemCore.Watch({ converter: pacemCore.PropertyConverters.Number })
|
|
1290
|
+
], PacemColumnChartElement.prototype, "groupWidth", void 0);
|
|
1291
|
+
PacemColumnChartElement = __decorate$1([
|
|
1292
|
+
pacemCore.CustomElement({ tagName: pacemCore.P + '-column-chart' })
|
|
1293
|
+
], PacemColumnChartElement);
|
|
1294
|
+
|
|
1295
|
+
var __decorate = (undefined && undefined.__decorate) || function (decorators, target, key, desc) {
|
|
1296
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
1297
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
1298
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
1299
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
1300
|
+
};
|
|
1301
|
+
//namespace Pacem.Components.Charts {
|
|
1302
|
+
let PacemPieSliceElement = class PacemPieSliceElement extends pacemCore.Components.PacemItemElement {
|
|
1303
|
+
constructor() {
|
|
1304
|
+
super(...arguments);
|
|
1305
|
+
this._broadcastHandler = (e) => {
|
|
1306
|
+
this.emit(e);
|
|
1307
|
+
};
|
|
1308
|
+
}
|
|
1309
|
+
findContainer() {
|
|
1310
|
+
return pacemCore.CustomElementUtils.findAncestorOfType(this, PacemPieChartElement);
|
|
1311
|
+
}
|
|
1312
|
+
get chart() {
|
|
1313
|
+
return this.container;
|
|
1314
|
+
}
|
|
1315
|
+
/** Returns the center of mass (point) of this pie/doughnut slice, in absolute coords (pixels). */
|
|
1316
|
+
getCenterOfMass() {
|
|
1317
|
+
const area = this.chart && this.chart.area;
|
|
1318
|
+
if (pacemCore.Utils.isNull(area)) {
|
|
1319
|
+
return null;
|
|
1320
|
+
}
|
|
1321
|
+
const rect = pacemCore.Utils.offset(area), center = { x: rect.width / 2 + rect.left, y: rect.top + rect.height / 2 }, r = Math.min(rect.width, rect.height) * .5;
|
|
1322
|
+
const pos = this.normalizedPolarCoords, angle = PI1_2 - pos.angle;
|
|
1323
|
+
return {
|
|
1324
|
+
y: center.y - r * pos.radius * Math.sin(angle),
|
|
1325
|
+
x: center.x + r * pos.radius * Math.cos(angle)
|
|
1326
|
+
};
|
|
1327
|
+
}
|
|
1328
|
+
propertyChangedCallback(name, old, val, first) {
|
|
1329
|
+
super.propertyChangedCallback(name, old, val, first);
|
|
1330
|
+
if (!first && this.chart && name != 'normalizedPolarCoords')
|
|
1331
|
+
this.chart.draw();
|
|
1332
|
+
}
|
|
1333
|
+
/** @internal */
|
|
1334
|
+
_assignUi(el) {
|
|
1335
|
+
const ui = this._ui = el;
|
|
1336
|
+
MANAGED_EVENTS.forEach(type => {
|
|
1337
|
+
ui.addEventListener(type, this._broadcastHandler);
|
|
1338
|
+
});
|
|
1339
|
+
}
|
|
1340
|
+
/** @internal */
|
|
1341
|
+
_disposeUi() {
|
|
1342
|
+
if (!pacemCore.Utils.isNull(this._ui)) {
|
|
1343
|
+
MANAGED_EVENTS.forEach(type => {
|
|
1344
|
+
this._ui.removeEventListener(type, this._broadcastHandler);
|
|
1345
|
+
});
|
|
1346
|
+
}
|
|
1347
|
+
}
|
|
1348
|
+
};
|
|
1349
|
+
__decorate([
|
|
1350
|
+
pacemCore.Watch({ converter: pacemCore.PropertyConverters.Number })
|
|
1351
|
+
], PacemPieSliceElement.prototype, "value", void 0);
|
|
1352
|
+
__decorate([
|
|
1353
|
+
pacemCore.Watch({ converter: pacemCore.PropertyConverters.String })
|
|
1354
|
+
], PacemPieSliceElement.prototype, "label", void 0);
|
|
1355
|
+
__decorate([
|
|
1356
|
+
pacemCore.Watch({ converter: pacemCore.PropertyConverters.String })
|
|
1357
|
+
], PacemPieSliceElement.prototype, "color", void 0);
|
|
1358
|
+
__decorate([
|
|
1359
|
+
pacemCore.Watch({ converter: pacemCore.PropertyConverters.Json })
|
|
1360
|
+
], PacemPieSliceElement.prototype, "normalizedPolarCoords", void 0);
|
|
1361
|
+
PacemPieSliceElement = __decorate([
|
|
1362
|
+
pacemCore.CustomElement({ tagName: pacemCore.P + '-pie-slice' })
|
|
1363
|
+
], PacemPieSliceElement);
|
|
1364
|
+
const TWO_PI = Math.PI * 2;
|
|
1365
|
+
const PI1_2 = Math.PI * .5;
|
|
1366
|
+
let PacemPieChartElement = class PacemPieChartElement extends pacemCore.Components.PacemItemsContainerElement {
|
|
1367
|
+
constructor() {
|
|
1368
|
+
super();
|
|
1369
|
+
/** Set a value between 0 and 1, for doughnut appearance. */
|
|
1370
|
+
this.cutout = .0;
|
|
1371
|
+
/** Distance between two consecutive slices, in pixels. */
|
|
1372
|
+
this.slicePadding = .0;
|
|
1373
|
+
this._slices = new WeakMap();
|
|
1374
|
+
this._key = pacemCore.Utils.uniqueCode();
|
|
1375
|
+
}
|
|
1376
|
+
validate(item) {
|
|
1377
|
+
return item instanceof PacemPieSliceElement;
|
|
1378
|
+
}
|
|
1379
|
+
viewActivatedCallback() {
|
|
1380
|
+
super.viewActivatedCallback();
|
|
1381
|
+
this.draw();
|
|
1382
|
+
}
|
|
1383
|
+
propertyChangedCallback(name, old, val, first) {
|
|
1384
|
+
super.propertyChangedCallback(name, old, val, first);
|
|
1385
|
+
if (name === 'target') {
|
|
1386
|
+
this._g = null;
|
|
1387
|
+
this._div && this._div.remove();
|
|
1388
|
+
const eold = old;
|
|
1389
|
+
if (eold) {
|
|
1390
|
+
eold.classList.remove(pacemCore.PCSS + '-chart-area');
|
|
1391
|
+
eold.innerHTML = '';
|
|
1392
|
+
}
|
|
1393
|
+
this.draw();
|
|
1394
|
+
}
|
|
1395
|
+
else if (!first && (name === 'cutout' || name === 'items' || name === 'slicePadding'))
|
|
1396
|
+
this.draw();
|
|
1397
|
+
}
|
|
1398
|
+
get area() {
|
|
1399
|
+
return this._svg;
|
|
1400
|
+
}
|
|
1401
|
+
disconnectedCallback() {
|
|
1402
|
+
this._div && this._div.remove();
|
|
1403
|
+
super.disconnectedCallback();
|
|
1404
|
+
}
|
|
1405
|
+
_ensureArea() {
|
|
1406
|
+
if (pacemCore.Utils.isNull(this._g)) {
|
|
1407
|
+
let div = this.target || (this._div = document.createElement('div'));
|
|
1408
|
+
div.classList.add(pacemCore.PCSS + '-chart-area');
|
|
1409
|
+
let svg = this._svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
|
|
1410
|
+
svg.setAttribute('pacem', '');
|
|
1411
|
+
svg.setAttribute('preserveAspectRatio', 'xMidYMid meet');
|
|
1412
|
+
svg.setAttribute('viewBox', '0 0 100 100');
|
|
1413
|
+
//svg.style.height =
|
|
1414
|
+
// svg.style.width = '100%';
|
|
1415
|
+
svg.classList.add(pacemCore.PCSS + '-pie-chart');
|
|
1416
|
+
let defs = document.createElementNS('http://www.w3.org/2000/svg', 'defs');
|
|
1417
|
+
let g = this._g = document.createElementNS('http://www.w3.org/2000/svg', 'g');
|
|
1418
|
+
svg.appendChild(defs);
|
|
1419
|
+
svg.appendChild(g);
|
|
1420
|
+
div.appendChild(svg);
|
|
1421
|
+
this._div &&
|
|
1422
|
+
this.parentElement.insertBefore(div, this);
|
|
1423
|
+
}
|
|
1424
|
+
if (this.maskBasedRendering) {
|
|
1425
|
+
if (pacemCore.Utils.isNull(this._mask)) {
|
|
1426
|
+
let mask = document.createElementNS('http://www.w3.org/2000/svg', 'mask');
|
|
1427
|
+
mask.id = 'pie_mask_' + this._key;
|
|
1428
|
+
let rect = document.createElementNS('http://www.w3.org/2000/svg', 'rect');
|
|
1429
|
+
rect.setAttribute('x', '0');
|
|
1430
|
+
rect.setAttribute('y', '0');
|
|
1431
|
+
rect.setAttribute('width', '100');
|
|
1432
|
+
rect.setAttribute('height', '100');
|
|
1433
|
+
rect.setAttribute('fill', '#fff');
|
|
1434
|
+
let circle = this._mask = document.createElementNS('http://www.w3.org/2000/svg', 'circle');
|
|
1435
|
+
circle.setAttribute('cx', '50');
|
|
1436
|
+
circle.setAttribute('cy', '50');
|
|
1437
|
+
const defs = this._svg.firstElementChild;
|
|
1438
|
+
mask.appendChild(rect);
|
|
1439
|
+
mask.appendChild(circle);
|
|
1440
|
+
defs.appendChild(mask);
|
|
1441
|
+
this._g.setAttribute('mask', `url(#${mask.id})`);
|
|
1442
|
+
}
|
|
1443
|
+
const cutout = 50.0 * this._safeCutout;
|
|
1444
|
+
this._mask.setAttribute('r', `${cutout}`);
|
|
1445
|
+
}
|
|
1446
|
+
else {
|
|
1447
|
+
if (!pacemCore.Utils.isNull(this._mask)) {
|
|
1448
|
+
this._svg.firstElementChild.innerHTML = '';
|
|
1449
|
+
this._mask = null;
|
|
1450
|
+
this._g.removeAttribute('mask');
|
|
1451
|
+
}
|
|
1452
|
+
}
|
|
1453
|
+
// has padded slices?
|
|
1454
|
+
const paddingPixels = this._safeSlicePaddingPixels;
|
|
1455
|
+
if (paddingPixels > 0) {
|
|
1456
|
+
const matrix = this._svg.getCTM();
|
|
1457
|
+
const paddingUnits = (paddingPixels / matrix.a).roundoff();
|
|
1458
|
+
const maskId = this._sliceMaskId;
|
|
1459
|
+
const defs = this._svg.firstElementChild;
|
|
1460
|
+
let mask = defs.querySelector(`#${maskId}`);
|
|
1461
|
+
if (pacemCore.Utils.isNull(mask)) {
|
|
1462
|
+
mask = document.createElementNS('http://www.w3.org/2000/svg', 'mask');
|
|
1463
|
+
mask.id = maskId;
|
|
1464
|
+
defs.appendChild(mask);
|
|
1465
|
+
}
|
|
1466
|
+
else {
|
|
1467
|
+
mask.innerHTML = '';
|
|
1468
|
+
}
|
|
1469
|
+
const maskPath = document.createElementNS('http://www.w3.org/2000/svg', 'path');
|
|
1470
|
+
const d = `M${(50 + paddingUnits)},0 H100 V100 H0 V0 H50 V50 h${paddingUnits} Z`;
|
|
1471
|
+
maskPath.setAttribute('d', d);
|
|
1472
|
+
maskPath.setAttribute('fill', '#fff');
|
|
1473
|
+
mask.appendChild(maskPath);
|
|
1474
|
+
}
|
|
1475
|
+
return this._g;
|
|
1476
|
+
}
|
|
1477
|
+
get _sliceMaskId() {
|
|
1478
|
+
return 'pieslice_mask_' + this._key;
|
|
1479
|
+
}
|
|
1480
|
+
get _safeCutout() {
|
|
1481
|
+
return Math.min(1, Math.max(0, this.cutout)) || .0;
|
|
1482
|
+
}
|
|
1483
|
+
get _safeSlicePaddingPixels() {
|
|
1484
|
+
return Math.max(0, this.slicePadding ?? 0);
|
|
1485
|
+
}
|
|
1486
|
+
_drawSlice(path, slice, whole, partial) {
|
|
1487
|
+
const largeFlag = slice.value > .5 * whole ? 1 : 0;
|
|
1488
|
+
const angle = TWO_PI * slice.value / whole;
|
|
1489
|
+
const rot = 360.0 * partial / whole;
|
|
1490
|
+
const x = (slice.value >= whole) ? /* handling single slice */ 49.9 : 50 * (1 + Math.sin(angle));
|
|
1491
|
+
const y = 50 * (1 - Math.cos(angle));
|
|
1492
|
+
// set geometry data
|
|
1493
|
+
const c = this._safeCutout, r = c * 50.0;
|
|
1494
|
+
path.style.fill = slice.color; //.setAttribute('fill', slice.color || (path.getAttribute('fill') || '#000'));
|
|
1495
|
+
//path.setAttribute('transform', `rotate(${rot} 50 50)`);
|
|
1496
|
+
path.style.transform = `rotate(${rot}deg)`;
|
|
1497
|
+
path.style.transformOrigin = '50% 50%';
|
|
1498
|
+
const d = this.maskBasedRendering ?
|
|
1499
|
+
`M50,50 V0 A50,50 0 ${largeFlag} 1 ${x} ${y} L50,50 Z` :
|
|
1500
|
+
`M50,0 A50,50 0 ${largeFlag} 1 ${x} ${y} l${(50 - x) * (1.0 - c)},${(50 - y) * (1.0 - c)} A${r},${r} 0 ${largeFlag} 0 50,${(50 - r)} Z`;
|
|
1501
|
+
path.setAttribute('d', d);
|
|
1502
|
+
slice.normalizedPolarCoords = { radius: c + Math.SQRT1_2 * (1.0 - c), angle: /* deg to rad */ Math.PI * rot / 180.0 + angle / 2 };
|
|
1503
|
+
}
|
|
1504
|
+
_isSliceOk(slice) {
|
|
1505
|
+
return !slice.disabled //&& slice.value > 0
|
|
1506
|
+
;
|
|
1507
|
+
}
|
|
1508
|
+
_padSlice(path) {
|
|
1509
|
+
const paddingPixels = this._safeSlicePaddingPixels;
|
|
1510
|
+
if (paddingPixels > 0) {
|
|
1511
|
+
path.setAttribute('mask', `url(#${this._sliceMaskId})`);
|
|
1512
|
+
}
|
|
1513
|
+
else {
|
|
1514
|
+
path.removeAttribute('mask');
|
|
1515
|
+
}
|
|
1516
|
+
}
|
|
1517
|
+
draw() {
|
|
1518
|
+
const g = this._ensureArea(), pCount = g.children.length, chartArea = this._svg.parentElement;
|
|
1519
|
+
let ndx = 0, sum = .0, progress = .0;
|
|
1520
|
+
if (pacemCore.Utils.isNullOrEmpty(this.items)) {
|
|
1521
|
+
g.innerHTML = '';
|
|
1522
|
+
pacemCore.Utils.removeClass(chartArea, 'chart-has-data');
|
|
1523
|
+
}
|
|
1524
|
+
else {
|
|
1525
|
+
pacemCore.Utils.addClass(chartArea, 'chart-has-data');
|
|
1526
|
+
for (let slice of this.items) {
|
|
1527
|
+
if (!this._isSliceOk(slice)) {
|
|
1528
|
+
continue;
|
|
1529
|
+
}
|
|
1530
|
+
sum += slice.value;
|
|
1531
|
+
}
|
|
1532
|
+
if (sum <= 0) {
|
|
1533
|
+
g.innerHTML = '';
|
|
1534
|
+
pacemCore.Utils.removeClass(chartArea, 'chart-has-data');
|
|
1535
|
+
}
|
|
1536
|
+
else {
|
|
1537
|
+
for (let slice of this.items) {
|
|
1538
|
+
if (!this._isSliceOk(slice)) {
|
|
1539
|
+
continue;
|
|
1540
|
+
}
|
|
1541
|
+
let p;
|
|
1542
|
+
if (ndx >= pCount) {
|
|
1543
|
+
const g_n = document.createElementNS('http://www.w3.org/2000/svg', 'g');
|
|
1544
|
+
g_n.setAttribute('class', 'chart-series ' + pacemCore.PCSS + '-pie-slice' + (pacemCore.Utils.isNullOrEmpty(slice.className) ? '' : (' ' + slice.className)));
|
|
1545
|
+
p = document.createElementNS('http://www.w3.org/2000/svg', 'path');
|
|
1546
|
+
g_n.appendChild(p);
|
|
1547
|
+
g.appendChild(g_n);
|
|
1548
|
+
// add listeners (click, mouse-..., touch-...)
|
|
1549
|
+
slice._assignUi(g_n);
|
|
1550
|
+
this._slices.set(g_n, slice);
|
|
1551
|
+
}
|
|
1552
|
+
else {
|
|
1553
|
+
p = g.children.item(ndx).firstElementChild;
|
|
1554
|
+
}
|
|
1555
|
+
if (slice.value > 0) {
|
|
1556
|
+
this._drawSlice(p, slice, sum, progress);
|
|
1557
|
+
this._padSlice(p);
|
|
1558
|
+
progress += slice.value;
|
|
1559
|
+
p.removeAttribute('display');
|
|
1560
|
+
}
|
|
1561
|
+
else {
|
|
1562
|
+
p.setAttribute('display', 'none');
|
|
1563
|
+
}
|
|
1564
|
+
ndx++;
|
|
1565
|
+
}
|
|
1566
|
+
}
|
|
1567
|
+
}
|
|
1568
|
+
while (ndx < g.children.length) {
|
|
1569
|
+
const slices = this._slices, g_n = g.children.item(ndx);
|
|
1570
|
+
// remove listeners
|
|
1571
|
+
if (slices.has(g_n)) {
|
|
1572
|
+
slices.get(g_n)._disposeUi();
|
|
1573
|
+
slices.delete(g_n);
|
|
1574
|
+
}
|
|
1575
|
+
g.removeChild(g_n);
|
|
1576
|
+
}
|
|
1577
|
+
}
|
|
1578
|
+
};
|
|
1579
|
+
__decorate([
|
|
1580
|
+
pacemCore.Watch({ converter: pacemCore.PropertyConverters.Number })
|
|
1581
|
+
], PacemPieChartElement.prototype, "cutout", void 0);
|
|
1582
|
+
__decorate([
|
|
1583
|
+
pacemCore.Watch({ converter: pacemCore.PropertyConverters.Element })
|
|
1584
|
+
], PacemPieChartElement.prototype, "target", void 0);
|
|
1585
|
+
__decorate([
|
|
1586
|
+
pacemCore.Watch({ converter: pacemCore.PropertyConverters.Boolean })
|
|
1587
|
+
], PacemPieChartElement.prototype, "maskBasedRendering", void 0);
|
|
1588
|
+
__decorate([
|
|
1589
|
+
pacemCore.Watch({ converter: pacemCore.PropertyConverters.Number })
|
|
1590
|
+
], PacemPieChartElement.prototype, "slicePadding", void 0);
|
|
1591
|
+
__decorate([
|
|
1592
|
+
pacemCore.Debounce(true)
|
|
1593
|
+
], PacemPieChartElement.prototype, "draw", null);
|
|
1594
|
+
PacemPieChartElement = __decorate([
|
|
1595
|
+
pacemCore.CustomElement({ tagName: pacemCore.P + '-pie-chart' })
|
|
1596
|
+
], PacemPieChartElement);
|
|
1597
|
+
|
|
1598
|
+
var indexComponentsCharts = /*#__PURE__*/Object.freeze({
|
|
1599
|
+
__proto__: null,
|
|
1600
|
+
ChartEvent: ChartEvent,
|
|
1601
|
+
MANAGED_EVENTS: MANAGED_EVENTS,
|
|
1602
|
+
get PacemChartElement () { return PacemChartElement; },
|
|
1603
|
+
get PacemChartSeriesElement () { return PacemChartSeriesElement; },
|
|
1604
|
+
get PacemColumnChartElement () { return PacemColumnChartElement; },
|
|
1605
|
+
get PacemPieChartElement () { return PacemPieChartElement; },
|
|
1606
|
+
get PacemPieSliceElement () { return PacemPieSliceElement; },
|
|
1607
|
+
PacemSeriesChartElement: PacemSeriesChartElement
|
|
1608
|
+
});
|
|
1609
|
+
|
|
1610
|
+
var indexComponents = /*#__PURE__*/Object.freeze({
|
|
1611
|
+
__proto__: null,
|
|
1612
|
+
Charts: indexComponentsCharts
|
|
1613
|
+
});
|
|
1614
|
+
|
|
1615
|
+
var Output = /*#__PURE__*/Object.freeze({
|
|
1616
|
+
__proto__: null,
|
|
1617
|
+
Components: indexComponents
|
|
1618
|
+
});
|
|
1619
|
+
|
|
1620
|
+
pacemFoundation.DeepMerger.merge(Output);
|
|
1621
|
+
|
|
1622
|
+
})(Pacem, Pacem);
|
|
1623
|
+
//# sourceMappingURL=pacem-charts.js.map
|