@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.
@@ -0,0 +1,306 @@
1
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
2
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
3
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
4
+ 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;
5
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
6
+ };
7
+ import { CustomElement, Watch, PropertyConverters, P, PCSS, Components, Utils, CustomElementUtils, Debounce } from '@pacem/pacem-core';
8
+ import { MANAGED_EVENTS } from './types';
9
+ //namespace Pacem.Components.Charts {
10
+ let PacemPieSliceElement = class PacemPieSliceElement extends Components.PacemItemElement {
11
+ constructor() {
12
+ super(...arguments);
13
+ this._broadcastHandler = (e) => {
14
+ this.emit(e);
15
+ };
16
+ }
17
+ findContainer() {
18
+ return CustomElementUtils.findAncestorOfType(this, PacemPieChartElement);
19
+ }
20
+ get chart() {
21
+ return this.container;
22
+ }
23
+ /** Returns the center of mass (point) of this pie/doughnut slice, in absolute coords (pixels). */
24
+ getCenterOfMass() {
25
+ const area = this.chart && this.chart.area;
26
+ if (Utils.isNull(area)) {
27
+ return null;
28
+ }
29
+ const rect = Utils.offset(area), center = { x: rect.width / 2 + rect.left, y: rect.top + rect.height / 2 }, r = Math.min(rect.width, rect.height) * .5;
30
+ const pos = this.normalizedPolarCoords, angle = PI1_2 - pos.angle;
31
+ return {
32
+ y: center.y - r * pos.radius * Math.sin(angle),
33
+ x: center.x + r * pos.radius * Math.cos(angle)
34
+ };
35
+ }
36
+ propertyChangedCallback(name, old, val, first) {
37
+ super.propertyChangedCallback(name, old, val, first);
38
+ if (!first && this.chart && name != 'normalizedPolarCoords')
39
+ this.chart.draw();
40
+ }
41
+ /** @internal */
42
+ _assignUi(el) {
43
+ const ui = this._ui = el;
44
+ MANAGED_EVENTS.forEach(type => {
45
+ ui.addEventListener(type, this._broadcastHandler);
46
+ });
47
+ }
48
+ /** @internal */
49
+ _disposeUi() {
50
+ if (!Utils.isNull(this._ui)) {
51
+ MANAGED_EVENTS.forEach(type => {
52
+ this._ui.removeEventListener(type, this._broadcastHandler);
53
+ });
54
+ }
55
+ }
56
+ };
57
+ __decorate([
58
+ Watch({ converter: PropertyConverters.Number })
59
+ ], PacemPieSliceElement.prototype, "value", void 0);
60
+ __decorate([
61
+ Watch({ converter: PropertyConverters.String })
62
+ ], PacemPieSliceElement.prototype, "label", void 0);
63
+ __decorate([
64
+ Watch({ converter: PropertyConverters.String })
65
+ ], PacemPieSliceElement.prototype, "color", void 0);
66
+ __decorate([
67
+ Watch({ converter: PropertyConverters.Json })
68
+ ], PacemPieSliceElement.prototype, "normalizedPolarCoords", void 0);
69
+ PacemPieSliceElement = __decorate([
70
+ CustomElement({ tagName: P + '-pie-slice' })
71
+ ], PacemPieSliceElement);
72
+ export { PacemPieSliceElement };
73
+ const TWO_PI = Math.PI * 2;
74
+ const PI1_2 = Math.PI * .5;
75
+ let PacemPieChartElement = class PacemPieChartElement extends Components.PacemItemsContainerElement {
76
+ constructor() {
77
+ super();
78
+ /** Set a value between 0 and 1, for doughnut appearance. */
79
+ this.cutout = .0;
80
+ /** Distance between two consecutive slices, in pixels. */
81
+ this.slicePadding = .0;
82
+ this._slices = new WeakMap();
83
+ this._key = Utils.uniqueCode();
84
+ }
85
+ validate(item) {
86
+ return item instanceof PacemPieSliceElement;
87
+ }
88
+ viewActivatedCallback() {
89
+ super.viewActivatedCallback();
90
+ this.draw();
91
+ }
92
+ propertyChangedCallback(name, old, val, first) {
93
+ super.propertyChangedCallback(name, old, val, first);
94
+ if (name === 'target') {
95
+ this._g = null;
96
+ this._div && this._div.remove();
97
+ const eold = old;
98
+ if (eold) {
99
+ eold.classList.remove(PCSS + '-chart-area');
100
+ eold.innerHTML = '';
101
+ }
102
+ this.draw();
103
+ }
104
+ else if (!first && (name === 'cutout' || name === 'items' || name === 'slicePadding'))
105
+ this.draw();
106
+ }
107
+ get area() {
108
+ return this._svg;
109
+ }
110
+ disconnectedCallback() {
111
+ this._div && this._div.remove();
112
+ super.disconnectedCallback();
113
+ }
114
+ _ensureArea() {
115
+ if (Utils.isNull(this._g)) {
116
+ let div = this.target || (this._div = document.createElement('div'));
117
+ div.classList.add(PCSS + '-chart-area');
118
+ let svg = this._svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
119
+ svg.setAttribute('pacem', '');
120
+ svg.setAttribute('preserveAspectRatio', 'xMidYMid meet');
121
+ svg.setAttribute('viewBox', '0 0 100 100');
122
+ //svg.style.height =
123
+ // svg.style.width = '100%';
124
+ svg.classList.add(PCSS + '-pie-chart');
125
+ let defs = document.createElementNS('http://www.w3.org/2000/svg', 'defs');
126
+ let g = this._g = document.createElementNS('http://www.w3.org/2000/svg', 'g');
127
+ svg.appendChild(defs);
128
+ svg.appendChild(g);
129
+ div.appendChild(svg);
130
+ this._div &&
131
+ this.parentElement.insertBefore(div, this);
132
+ }
133
+ if (this.maskBasedRendering) {
134
+ if (Utils.isNull(this._mask)) {
135
+ let mask = document.createElementNS('http://www.w3.org/2000/svg', 'mask');
136
+ mask.id = 'pie_mask_' + this._key;
137
+ let rect = document.createElementNS('http://www.w3.org/2000/svg', 'rect');
138
+ rect.setAttribute('x', '0');
139
+ rect.setAttribute('y', '0');
140
+ rect.setAttribute('width', '100');
141
+ rect.setAttribute('height', '100');
142
+ rect.setAttribute('fill', '#fff');
143
+ let circle = this._mask = document.createElementNS('http://www.w3.org/2000/svg', 'circle');
144
+ circle.setAttribute('cx', '50');
145
+ circle.setAttribute('cy', '50');
146
+ const defs = this._svg.firstElementChild;
147
+ mask.appendChild(rect);
148
+ mask.appendChild(circle);
149
+ defs.appendChild(mask);
150
+ this._g.setAttribute('mask', `url(#${mask.id})`);
151
+ }
152
+ const cutout = 50.0 * this._safeCutout;
153
+ this._mask.setAttribute('r', `${cutout}`);
154
+ }
155
+ else {
156
+ if (!Utils.isNull(this._mask)) {
157
+ this._svg.firstElementChild.innerHTML = '';
158
+ this._mask = null;
159
+ this._g.removeAttribute('mask');
160
+ }
161
+ }
162
+ // has padded slices?
163
+ const paddingPixels = this._safeSlicePaddingPixels;
164
+ if (paddingPixels > 0) {
165
+ const matrix = this._svg.getCTM();
166
+ const paddingUnits = (paddingPixels / matrix.a).roundoff();
167
+ const maskId = this._sliceMaskId;
168
+ const defs = this._svg.firstElementChild;
169
+ let mask = defs.querySelector(`#${maskId}`);
170
+ if (Utils.isNull(mask)) {
171
+ mask = document.createElementNS('http://www.w3.org/2000/svg', 'mask');
172
+ mask.id = maskId;
173
+ defs.appendChild(mask);
174
+ }
175
+ else {
176
+ mask.innerHTML = '';
177
+ }
178
+ const maskPath = document.createElementNS('http://www.w3.org/2000/svg', 'path');
179
+ const d = `M${(50 + paddingUnits)},0 H100 V100 H0 V0 H50 V50 h${paddingUnits} Z`;
180
+ maskPath.setAttribute('d', d);
181
+ maskPath.setAttribute('fill', '#fff');
182
+ mask.appendChild(maskPath);
183
+ }
184
+ return this._g;
185
+ }
186
+ get _sliceMaskId() {
187
+ return 'pieslice_mask_' + this._key;
188
+ }
189
+ get _safeCutout() {
190
+ return Math.min(1, Math.max(0, this.cutout)) || .0;
191
+ }
192
+ get _safeSlicePaddingPixels() {
193
+ return Math.max(0, this.slicePadding ?? 0);
194
+ }
195
+ _drawSlice(path, slice, whole, partial) {
196
+ const largeFlag = slice.value > .5 * whole ? 1 : 0;
197
+ const angle = TWO_PI * slice.value / whole;
198
+ const rot = 360.0 * partial / whole;
199
+ const x = (slice.value >= whole) ? /* handling single slice */ 49.9 : 50 * (1 + Math.sin(angle));
200
+ const y = 50 * (1 - Math.cos(angle));
201
+ // set geometry data
202
+ const c = this._safeCutout, r = c * 50.0;
203
+ path.style.fill = slice.color; //.setAttribute('fill', slice.color || (path.getAttribute('fill') || '#000'));
204
+ //path.setAttribute('transform', `rotate(${rot} 50 50)`);
205
+ path.style.transform = `rotate(${rot}deg)`;
206
+ path.style.transformOrigin = '50% 50%';
207
+ const d = this.maskBasedRendering ?
208
+ `M50,50 V0 A50,50 0 ${largeFlag} 1 ${x} ${y} L50,50 Z` :
209
+ `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`;
210
+ path.setAttribute('d', d);
211
+ slice.normalizedPolarCoords = { radius: c + Math.SQRT1_2 * (1.0 - c), angle: /* deg to rad */ Math.PI * rot / 180.0 + angle / 2 };
212
+ }
213
+ _isSliceOk(slice) {
214
+ return !slice.disabled //&& slice.value > 0
215
+ ;
216
+ }
217
+ _padSlice(path) {
218
+ const paddingPixels = this._safeSlicePaddingPixels;
219
+ if (paddingPixels > 0) {
220
+ path.setAttribute('mask', `url(#${this._sliceMaskId})`);
221
+ }
222
+ else {
223
+ path.removeAttribute('mask');
224
+ }
225
+ }
226
+ draw() {
227
+ const g = this._ensureArea(), pCount = g.children.length, chartArea = this._svg.parentElement;
228
+ let ndx = 0, sum = .0, progress = .0;
229
+ if (Utils.isNullOrEmpty(this.items)) {
230
+ g.innerHTML = '';
231
+ Utils.removeClass(chartArea, 'chart-has-data');
232
+ }
233
+ else {
234
+ Utils.addClass(chartArea, 'chart-has-data');
235
+ for (let slice of this.items) {
236
+ if (!this._isSliceOk(slice)) {
237
+ continue;
238
+ }
239
+ sum += slice.value;
240
+ }
241
+ if (sum <= 0) {
242
+ g.innerHTML = '';
243
+ Utils.removeClass(chartArea, 'chart-has-data');
244
+ }
245
+ else {
246
+ for (let slice of this.items) {
247
+ if (!this._isSliceOk(slice)) {
248
+ continue;
249
+ }
250
+ let p;
251
+ if (ndx >= pCount) {
252
+ const g_n = document.createElementNS('http://www.w3.org/2000/svg', 'g');
253
+ g_n.setAttribute('class', 'chart-series ' + PCSS + '-pie-slice' + (Utils.isNullOrEmpty(slice.className) ? '' : (' ' + slice.className)));
254
+ p = document.createElementNS('http://www.w3.org/2000/svg', 'path');
255
+ g_n.appendChild(p);
256
+ g.appendChild(g_n);
257
+ // add listeners (click, mouse-..., touch-...)
258
+ slice._assignUi(g_n);
259
+ this._slices.set(g_n, slice);
260
+ }
261
+ else {
262
+ p = g.children.item(ndx).firstElementChild;
263
+ }
264
+ if (slice.value > 0) {
265
+ this._drawSlice(p, slice, sum, progress);
266
+ this._padSlice(p);
267
+ progress += slice.value;
268
+ p.removeAttribute('display');
269
+ }
270
+ else {
271
+ p.setAttribute('display', 'none');
272
+ }
273
+ ndx++;
274
+ }
275
+ }
276
+ }
277
+ while (ndx < g.children.length) {
278
+ const slices = this._slices, g_n = g.children.item(ndx);
279
+ // remove listeners
280
+ if (slices.has(g_n)) {
281
+ slices.get(g_n)._disposeUi();
282
+ slices.delete(g_n);
283
+ }
284
+ g.removeChild(g_n);
285
+ }
286
+ }
287
+ };
288
+ __decorate([
289
+ Watch({ converter: PropertyConverters.Number })
290
+ ], PacemPieChartElement.prototype, "cutout", void 0);
291
+ __decorate([
292
+ Watch({ converter: PropertyConverters.Element })
293
+ ], PacemPieChartElement.prototype, "target", void 0);
294
+ __decorate([
295
+ Watch({ converter: PropertyConverters.Boolean })
296
+ ], PacemPieChartElement.prototype, "maskBasedRendering", void 0);
297
+ __decorate([
298
+ Watch({ converter: PropertyConverters.Number })
299
+ ], PacemPieChartElement.prototype, "slicePadding", void 0);
300
+ __decorate([
301
+ Debounce(true)
302
+ ], PacemPieChartElement.prototype, "draw", null);
303
+ PacemPieChartElement = __decorate([
304
+ CustomElement({ tagName: P + '-pie-chart' })
305
+ ], PacemPieChartElement);
306
+ export { PacemPieChartElement };