@rfprodz/client-d3-charts 1.0.0
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 +21 -0
- package/README.md +88 -0
- package/esm2020/index.mjs +5 -0
- package/esm2020/lib/client-d3-charts.module.mjs +47 -0
- package/esm2020/lib/components/bar-chart/bar-chart.component.mjs +87 -0
- package/esm2020/lib/components/chart-examples/chart-examples.component.mjs +181 -0
- package/esm2020/lib/components/force-directed-chart/force-directed-chart.component.mjs +92 -0
- package/esm2020/lib/components/index.mjs +7 -0
- package/esm2020/lib/components/line-chart/line-chart.component.mjs +91 -0
- package/esm2020/lib/components/pie-chart/pie-chart.component.mjs +90 -0
- package/esm2020/lib/components/radar-chart/radar-chart.component.mjs +102 -0
- package/esm2020/lib/interfaces/bar-chart.interface.mjs +2 -0
- package/esm2020/lib/interfaces/force-directed-chart.interface.mjs +2 -0
- package/esm2020/lib/interfaces/index.mjs +6 -0
- package/esm2020/lib/interfaces/line-chart.interface.mjs +2 -0
- package/esm2020/lib/interfaces/pie-chart.interface.mjs +2 -0
- package/esm2020/lib/interfaces/radar-chart.interface.mjs +2 -0
- package/esm2020/lib/providers/d3-chart-factory.provider.mjs +18 -0
- package/esm2020/lib/util/bar-chart.util.mjs +255 -0
- package/esm2020/lib/util/configuration.util.mjs +31 -0
- package/esm2020/lib/util/force-directed-chart.util.mjs +255 -0
- package/esm2020/lib/util/index.mjs +6 -0
- package/esm2020/lib/util/line-chart.util.mjs +266 -0
- package/esm2020/lib/util/pie-chart.util.mjs +89 -0
- package/esm2020/lib/util/radar-chart.util.mjs +295 -0
- package/esm2020/rfprodz-client-d3-charts.mjs +5 -0
- package/fesm2015/rfprodz-client-d3-charts.mjs +1868 -0
- package/fesm2015/rfprodz-client-d3-charts.mjs.map +1 -0
- package/fesm2020/rfprodz-client-d3-charts.mjs +1855 -0
- package/fesm2020/rfprodz-client-d3-charts.mjs.map +1 -0
- package/index.d.ts +4 -0
- package/lib/client-d3-charts.module.d.ts +13 -0
- package/lib/components/bar-chart/bar-chart.component.d.ts +49 -0
- package/lib/components/chart-examples/chart-examples.component.d.ts +60 -0
- package/lib/components/force-directed-chart/force-directed-chart.component.d.ts +49 -0
- package/lib/components/index.d.ts +6 -0
- package/lib/components/line-chart/line-chart.component.d.ts +49 -0
- package/lib/components/pie-chart/pie-chart.component.d.ts +49 -0
- package/lib/components/radar-chart/radar-chart.component.d.ts +49 -0
- package/lib/interfaces/bar-chart.interface.d.ts +25 -0
- package/lib/interfaces/force-directed-chart.interface.d.ts +50 -0
- package/lib/interfaces/index.d.ts +5 -0
- package/lib/interfaces/line-chart.interface.d.ts +28 -0
- package/lib/interfaces/pie-chart.interface.d.ts +19 -0
- package/lib/interfaces/radar-chart.interface.d.ts +30 -0
- package/lib/providers/d3-chart-factory.provider.d.ts +15 -0
- package/lib/util/bar-chart.util.d.ts +14 -0
- package/lib/util/configuration.util.d.ts +7 -0
- package/lib/util/force-directed-chart.util.d.ts +14 -0
- package/lib/util/index.d.ts +5 -0
- package/lib/util/line-chart.util.d.ts +14 -0
- package/lib/util/pie-chart.util.d.ts +14 -0
- package/lib/util/radar-chart.util.d.ts +14 -0
- package/package.json +53 -0
|
@@ -0,0 +1,1855 @@
|
|
|
1
|
+
import * as i2 from '@angular/common';
|
|
2
|
+
import { DOCUMENT, CommonModule } from '@angular/common';
|
|
3
|
+
import * as i0 from '@angular/core';
|
|
4
|
+
import { InjectionToken, Component, ChangeDetectionStrategy, Inject, Input, ViewChild, NgModule } from '@angular/core';
|
|
5
|
+
import * as d3 from 'd3';
|
|
6
|
+
import * as i1 from '@angular/cdk/layout';
|
|
7
|
+
import { Breakpoints } from '@angular/cdk/layout';
|
|
8
|
+
import { map, switchMap, timer, first } from 'rxjs';
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Generates a configuration object based on a defaut configuration and an options object.
|
|
12
|
+
* @param config the default object with all properties
|
|
13
|
+
* @param options the input object
|
|
14
|
+
* @param result the output object
|
|
15
|
+
*/
|
|
16
|
+
const generateConfiguration = (config, options, result) => {
|
|
17
|
+
const defaultConfiguration = config;
|
|
18
|
+
if (typeof options === 'undefined') {
|
|
19
|
+
return config;
|
|
20
|
+
}
|
|
21
|
+
const keys = Object.keys(defaultConfiguration);
|
|
22
|
+
for (const key of keys) {
|
|
23
|
+
const defaultValue = defaultConfiguration[key];
|
|
24
|
+
const value = options[key];
|
|
25
|
+
const typedKey = key;
|
|
26
|
+
if (typeof defaultValue === 'string' || typeof defaultValue === 'number' || typeof defaultValue === 'boolean') {
|
|
27
|
+
result[typedKey] = typeof value !== 'undefined' ? value : defaultValue;
|
|
28
|
+
}
|
|
29
|
+
else if (defaultValue instanceof Function) {
|
|
30
|
+
result[typedKey] = defaultValue;
|
|
31
|
+
}
|
|
32
|
+
else if (typeof defaultValue === 'object' && defaultValue !== null) {
|
|
33
|
+
const nestedDefaultObject = defaultValue;
|
|
34
|
+
const nestedObject = value;
|
|
35
|
+
result[typedKey] = generateConfiguration(nestedDefaultObject, nestedObject, {});
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
return result;
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* The bar chart default configuration.
|
|
43
|
+
*/
|
|
44
|
+
const defaultBarChartConfig = Object.freeze({
|
|
45
|
+
chartTitle: '',
|
|
46
|
+
width: 350,
|
|
47
|
+
height: 350,
|
|
48
|
+
margin: {
|
|
49
|
+
top: 70,
|
|
50
|
+
right: 50,
|
|
51
|
+
bottom: 50,
|
|
52
|
+
left: 50,
|
|
53
|
+
},
|
|
54
|
+
transitionDuration: 400,
|
|
55
|
+
xAxisPadding: 0.4,
|
|
56
|
+
xAxisTitle: '',
|
|
57
|
+
yAxisTitle: '',
|
|
58
|
+
yAxisTicks: 10,
|
|
59
|
+
displayAxisLabels: true,
|
|
60
|
+
labelTextWrapWidth: 60,
|
|
61
|
+
color: d3.scaleOrdinal(d3.schemeCategory10),
|
|
62
|
+
});
|
|
63
|
+
/**
|
|
64
|
+
* Creates a container for the bar chart.
|
|
65
|
+
* @param container the chart container
|
|
66
|
+
* @param config the chart configuration
|
|
67
|
+
* @returns the object with the svg element and the g element
|
|
68
|
+
*/
|
|
69
|
+
const createContainer$4 = (container, config) => {
|
|
70
|
+
const id = container.nativeElement.id ?? 'bar-0';
|
|
71
|
+
d3.select(`#${id}`).select('svg').remove();
|
|
72
|
+
const svg = d3
|
|
73
|
+
.select(`#${id}`)
|
|
74
|
+
.append('svg')
|
|
75
|
+
.attr('width', config.width + config.margin.left + config.margin.right)
|
|
76
|
+
.attr('height', config.height + config.margin.top + config.margin.bottom)
|
|
77
|
+
.attr('class', id);
|
|
78
|
+
const g = svg.append('g').attr('transform', `translate(${config.margin.left},${config.margin.top / 2})`);
|
|
79
|
+
return { svg, g };
|
|
80
|
+
};
|
|
81
|
+
/**
|
|
82
|
+
* Wraps the bar chart axis labels text.
|
|
83
|
+
* @param svgText the svg text elements
|
|
84
|
+
* @param width the chart axis label width
|
|
85
|
+
*/
|
|
86
|
+
const wrapSvgText$2 = (svgText, width) => {
|
|
87
|
+
svgText.each(function () {
|
|
88
|
+
const text = d3.select(this);
|
|
89
|
+
const words = text.text().split(/\s+/).reverse();
|
|
90
|
+
let line = [];
|
|
91
|
+
let lineNumber = 0;
|
|
92
|
+
const lineHeight = 1.4;
|
|
93
|
+
const y = text.attr('y');
|
|
94
|
+
const x = text.attr('x');
|
|
95
|
+
const dy = parseFloat(text.attr('dy') ?? 0);
|
|
96
|
+
let tspan = text.text(null).append('tspan').attr('x', x).attr('y', y).attr('dy', `${dy}em`); // axis label
|
|
97
|
+
let word = words.pop();
|
|
98
|
+
while (typeof word !== 'undefined') {
|
|
99
|
+
line.push(word ?? '');
|
|
100
|
+
tspan.text(line.join(' '));
|
|
101
|
+
if ((tspan.node()?.getComputedTextLength() ?? 0) > width) {
|
|
102
|
+
line.pop();
|
|
103
|
+
tspan.text(line.join(' '));
|
|
104
|
+
line = [word ?? ''];
|
|
105
|
+
lineNumber += 1;
|
|
106
|
+
tspan = text
|
|
107
|
+
.append('tspan')
|
|
108
|
+
.attr('x', 0)
|
|
109
|
+
.attr('y', y)
|
|
110
|
+
.attr('dy', `${lineNumber * lineHeight + dy}em`)
|
|
111
|
+
.text(word ?? '');
|
|
112
|
+
}
|
|
113
|
+
word = words.pop();
|
|
114
|
+
}
|
|
115
|
+
});
|
|
116
|
+
};
|
|
117
|
+
/**
|
|
118
|
+
* Creates the legend.
|
|
119
|
+
* @param g the svg g element
|
|
120
|
+
* @param config the chart configuration
|
|
121
|
+
*/
|
|
122
|
+
const createLegend$2 = (g, config) => {
|
|
123
|
+
if (config.displayAxisLabels && config.xAxisTitle !== '') {
|
|
124
|
+
g.append('g')
|
|
125
|
+
.attr('transform', `translate(0, ${config.height + config.margin.bottom})`)
|
|
126
|
+
.append('text')
|
|
127
|
+
.style('font-size', '12px')
|
|
128
|
+
.attr('class', 'legend')
|
|
129
|
+
.attr('dx', '1.5em')
|
|
130
|
+
.attr('dy', '1em')
|
|
131
|
+
.text(`x - ${config.xAxisTitle}`);
|
|
132
|
+
}
|
|
133
|
+
if (config.displayAxisLabels && config.yAxisTitle !== '') {
|
|
134
|
+
g.append('g')
|
|
135
|
+
.attr('transform', `translate(0, ${config.height + config.margin.bottom})`)
|
|
136
|
+
.append('text')
|
|
137
|
+
.style('font-size', '12px')
|
|
138
|
+
.attr('class', 'legend')
|
|
139
|
+
.attr('dx', '1.5em')
|
|
140
|
+
.attr('dy', '2.5em')
|
|
141
|
+
.text(`y - ${config.yAxisTitle}`);
|
|
142
|
+
}
|
|
143
|
+
if (config.chartTitle !== '') {
|
|
144
|
+
g.append('g')
|
|
145
|
+
.attr('transform', `translate(0, 0)`)
|
|
146
|
+
.append('text')
|
|
147
|
+
.style('font-size', '12px')
|
|
148
|
+
.attr('class', 'legend')
|
|
149
|
+
.attr('dx', '1.5em')
|
|
150
|
+
.attr('dy', '-2em')
|
|
151
|
+
.text(config.chartTitle);
|
|
152
|
+
}
|
|
153
|
+
};
|
|
154
|
+
/**
|
|
155
|
+
* Creates the x axis.
|
|
156
|
+
* @param g the svg g element
|
|
157
|
+
* @param x the x axis scale
|
|
158
|
+
* @param config the chart configuration
|
|
159
|
+
*/
|
|
160
|
+
const createAxisX$1 = (g, x, config) => {
|
|
161
|
+
const xLabels = g.append('g').attr('transform', `translate(0, ${config.height})`).call(d3.axisBottom(x)).append('text');
|
|
162
|
+
g.selectAll('text').call(wrapSvgText$2, config.labelTextWrapWidth);
|
|
163
|
+
if (config.displayAxisLabels) {
|
|
164
|
+
xLabels.attr('transform', `translate(${config.width}, 0)`).attr('class', 'legend').attr('dx', '1.5em').attr('dy', '0.7em').text('x');
|
|
165
|
+
}
|
|
166
|
+
};
|
|
167
|
+
/**
|
|
168
|
+
* Creates the y axis.
|
|
169
|
+
* @param g the svg g element
|
|
170
|
+
* @param y the y axis scale
|
|
171
|
+
* @param config the chart configuration
|
|
172
|
+
*/
|
|
173
|
+
const createAxisY$1 = (g, y, config) => {
|
|
174
|
+
const yLabels = g
|
|
175
|
+
.append('g')
|
|
176
|
+
.call(d3
|
|
177
|
+
.axisLeft(y)
|
|
178
|
+
.tickFormat(function (d) {
|
|
179
|
+
return `${d}`;
|
|
180
|
+
})
|
|
181
|
+
.ticks(config.yAxisTicks))
|
|
182
|
+
.append('text');
|
|
183
|
+
if (config.displayAxisLabels) {
|
|
184
|
+
yLabels.attr('dy', '-1.5em').attr('class', 'legend').text('y');
|
|
185
|
+
}
|
|
186
|
+
};
|
|
187
|
+
/**
|
|
188
|
+
* The mouse over event handler.
|
|
189
|
+
* @param self an svg rect element
|
|
190
|
+
* @param d the chart data node
|
|
191
|
+
* @param g the svg g element
|
|
192
|
+
* @param x the x axis scale
|
|
193
|
+
* @param y the y axis scale
|
|
194
|
+
* @param config the chart configuration
|
|
195
|
+
*/
|
|
196
|
+
const onMouseOver$1 = (self, d, g, x, y, config) => {
|
|
197
|
+
const widthModifier = 5;
|
|
198
|
+
d3.select(self)
|
|
199
|
+
.transition()
|
|
200
|
+
.duration(config.transitionDuration)
|
|
201
|
+
.attr('width', x.bandwidth() + widthModifier)
|
|
202
|
+
.attr('y', function () {
|
|
203
|
+
const modifier = 10;
|
|
204
|
+
return y(d.value) - modifier;
|
|
205
|
+
})
|
|
206
|
+
.attr('height', function () {
|
|
207
|
+
const modifier = 10;
|
|
208
|
+
return config.height - y(d.value) + modifier;
|
|
209
|
+
});
|
|
210
|
+
g.append('text')
|
|
211
|
+
.attr('class', 'val')
|
|
212
|
+
.style('font-size', '11px')
|
|
213
|
+
.attr('x', () => x(d.title) ?? '')
|
|
214
|
+
.attr('y', function () {
|
|
215
|
+
const modifier = 15;
|
|
216
|
+
return y(d.value) - modifier;
|
|
217
|
+
})
|
|
218
|
+
.text(() => d.value);
|
|
219
|
+
};
|
|
220
|
+
/**
|
|
221
|
+
* The mouse out event handler.
|
|
222
|
+
* @param self an svg rect element
|
|
223
|
+
* @param d the chart data node
|
|
224
|
+
* @param x the x axis scale
|
|
225
|
+
* @param y the y axis scale
|
|
226
|
+
* @param config the chart configuration
|
|
227
|
+
*/
|
|
228
|
+
const onMouseOut$1 = (self, d, x, y, config) => {
|
|
229
|
+
d3.select(self).attr('class', 'bar');
|
|
230
|
+
d3.select(self)
|
|
231
|
+
.transition()
|
|
232
|
+
.duration(config.transitionDuration)
|
|
233
|
+
.attr('width', x.bandwidth())
|
|
234
|
+
.attr('y', () => y(d.value) ?? 0)
|
|
235
|
+
.attr('height', () => config.height - (y(d.value) ?? 0));
|
|
236
|
+
d3.selectAll('.val').remove();
|
|
237
|
+
};
|
|
238
|
+
/**
|
|
239
|
+
* Draws the chart bars, and sets the mouse pointer events.
|
|
240
|
+
* @param g the svg g element
|
|
241
|
+
* @param x the x axis scale
|
|
242
|
+
* @param y the y axis scale
|
|
243
|
+
* @param config the chart configuration
|
|
244
|
+
* @param data the chart data
|
|
245
|
+
*/
|
|
246
|
+
const drawBarsAndSetPointerEvents = (g, x, y, config, data) => {
|
|
247
|
+
const duration = 400;
|
|
248
|
+
g.selectAll('.bar')
|
|
249
|
+
.data(data)
|
|
250
|
+
.enter()
|
|
251
|
+
.append('rect')
|
|
252
|
+
.attr('class', 'bar')
|
|
253
|
+
.style('fill', (d, i) => config.color(i.toString()))
|
|
254
|
+
.on('mouseover', function (event, d) {
|
|
255
|
+
return onMouseOver$1(this, d, g, x, y, config);
|
|
256
|
+
})
|
|
257
|
+
.on('mouseout', function (event, d) {
|
|
258
|
+
return onMouseOut$1(this, d, x, y, config);
|
|
259
|
+
})
|
|
260
|
+
.attr('x', d => x(d.title) ?? '')
|
|
261
|
+
.attr('y', d => y(d.value))
|
|
262
|
+
.attr('width', x.bandwidth())
|
|
263
|
+
.transition()
|
|
264
|
+
.ease(d3.easeLinear)
|
|
265
|
+
.duration(duration)
|
|
266
|
+
.delay(function (d, i) {
|
|
267
|
+
const multiplier = 50;
|
|
268
|
+
return i * multiplier;
|
|
269
|
+
})
|
|
270
|
+
.attr('height', d => config.height - y(d.value));
|
|
271
|
+
};
|
|
272
|
+
/**
|
|
273
|
+
* Draws the bar chart.
|
|
274
|
+
* @param container the chart container
|
|
275
|
+
* @param data the chart data
|
|
276
|
+
* @param options the chart options
|
|
277
|
+
* @returns the chart configuration
|
|
278
|
+
*/
|
|
279
|
+
const drawBarChart = (container, data, options) => {
|
|
280
|
+
const config = generateConfiguration(defaultBarChartConfig, options, {});
|
|
281
|
+
const { g } = createContainer$4(container, config);
|
|
282
|
+
const x = d3
|
|
283
|
+
.scaleBand([0, config.width])
|
|
284
|
+
.padding(config.xAxisPadding)
|
|
285
|
+
.domain(data.map(d => d.title));
|
|
286
|
+
const y = d3.scaleLinear([config.height, 0]).domain([0, d3.max(data, d => d.value) ?? 1]);
|
|
287
|
+
createAxisX$1(g, x, config);
|
|
288
|
+
createAxisY$1(g, y, config);
|
|
289
|
+
createLegend$2(g, config);
|
|
290
|
+
drawBarsAndSetPointerEvents(g, x, y, config, data);
|
|
291
|
+
return config;
|
|
292
|
+
};
|
|
293
|
+
|
|
294
|
+
/**
|
|
295
|
+
* The force directed chart default configuration.
|
|
296
|
+
*/
|
|
297
|
+
const defaultForceDirectedChartConfig = Object.freeze({
|
|
298
|
+
chartTitle: '',
|
|
299
|
+
width: 600,
|
|
300
|
+
height: 600,
|
|
301
|
+
centerCalcMod: 1.6,
|
|
302
|
+
charge: {
|
|
303
|
+
strength: -10,
|
|
304
|
+
theta: 0.6,
|
|
305
|
+
distanceMax: 2000,
|
|
306
|
+
},
|
|
307
|
+
distance: 75,
|
|
308
|
+
fontSize: 10,
|
|
309
|
+
collisionRadius: 30,
|
|
310
|
+
margin: {
|
|
311
|
+
top: 20,
|
|
312
|
+
right: 20,
|
|
313
|
+
bottom: 20,
|
|
314
|
+
left: 20,
|
|
315
|
+
},
|
|
316
|
+
strokeWidth: 1.5,
|
|
317
|
+
labelTextWrapWidth: 60,
|
|
318
|
+
color: d3.scaleOrdinal(d3.schemeCategory10),
|
|
319
|
+
});
|
|
320
|
+
/**
|
|
321
|
+
* The force durected chart tick handler.
|
|
322
|
+
* @param link chart links
|
|
323
|
+
* @param node chart nodes
|
|
324
|
+
* @param text chart text
|
|
325
|
+
* @returns rotation angle
|
|
326
|
+
*/
|
|
327
|
+
const ticked = (link, node, text) => {
|
|
328
|
+
if (typeof link !== 'undefined') {
|
|
329
|
+
link
|
|
330
|
+
.attr('x1', d => d.source.x ?? 0)
|
|
331
|
+
.attr('y1', d => d.source.y ?? 0)
|
|
332
|
+
.attr('x2', d => d.target.x ?? 0)
|
|
333
|
+
.attr('y2', d => d.target.y ?? 0);
|
|
334
|
+
}
|
|
335
|
+
if (typeof node !== 'undefined') {
|
|
336
|
+
node.attr('cx', d => d.x ?? 0).attr('cy', d => d.y ?? 0);
|
|
337
|
+
}
|
|
338
|
+
if (typeof text !== 'undefined') {
|
|
339
|
+
const dx = 10;
|
|
340
|
+
const dy = 5;
|
|
341
|
+
text.attr('x', d => (d.x ?? 0) + dx).attr('y', d => (d.y ?? 0) - dy);
|
|
342
|
+
}
|
|
343
|
+
return 'rotate(0)';
|
|
344
|
+
};
|
|
345
|
+
/**
|
|
346
|
+
* Creates a container for the force directed chart.
|
|
347
|
+
* @param container the chart container
|
|
348
|
+
* @param config the chart configuration
|
|
349
|
+
* @returns the object with the svg element and the g element
|
|
350
|
+
*/
|
|
351
|
+
const createContainer$3 = (container, config) => {
|
|
352
|
+
const id = container.nativeElement.id ?? 'force-directed-0';
|
|
353
|
+
d3.select(`#${id}`).select('svg').remove();
|
|
354
|
+
const svg = d3
|
|
355
|
+
.select(`#${id}`)
|
|
356
|
+
.append('svg')
|
|
357
|
+
.attr('width', config.width + config.margin.left + config.margin.right)
|
|
358
|
+
.attr('height', config.height + config.margin.top + config.margin.bottom)
|
|
359
|
+
.attr('class', id);
|
|
360
|
+
const g = svg
|
|
361
|
+
.append('g')
|
|
362
|
+
.attr('transform', `translate(${config.width / 2 + config.margin.left},${config.height / 2 + config.margin.top})`);
|
|
363
|
+
return { svg, g };
|
|
364
|
+
};
|
|
365
|
+
/**
|
|
366
|
+
* Applies the force directed chart data.
|
|
367
|
+
* @param g the svg g element
|
|
368
|
+
* @param data the chart data
|
|
369
|
+
*/
|
|
370
|
+
const applyChartData = (g, data) => {
|
|
371
|
+
const imageXY = 10;
|
|
372
|
+
g.append('defs')
|
|
373
|
+
.selectAll('pattern')
|
|
374
|
+
.data(data.entities)
|
|
375
|
+
.enter()
|
|
376
|
+
.append('pattern')
|
|
377
|
+
.attr('id', (val, i) => `img-${val.index}`)
|
|
378
|
+
.attr('x', 0)
|
|
379
|
+
.attr('y', 0)
|
|
380
|
+
.attr('height', val => {
|
|
381
|
+
const baseValue = 30;
|
|
382
|
+
return baseValue + val.linksCount * 2;
|
|
383
|
+
})
|
|
384
|
+
.attr('width', val => {
|
|
385
|
+
const baseValue = 30;
|
|
386
|
+
return baseValue + val.linksCount * 2;
|
|
387
|
+
})
|
|
388
|
+
.append('image')
|
|
389
|
+
.attr('x', imageXY)
|
|
390
|
+
.attr('y', imageXY)
|
|
391
|
+
.attr('height', val => {
|
|
392
|
+
const baseValue = 30;
|
|
393
|
+
return baseValue + val.linksCount * 2;
|
|
394
|
+
})
|
|
395
|
+
.attr('width', val => {
|
|
396
|
+
const baseValue = 30;
|
|
397
|
+
return baseValue + val.linksCount * 2;
|
|
398
|
+
})
|
|
399
|
+
.attr('xlink:href', val => val.img);
|
|
400
|
+
};
|
|
401
|
+
/**
|
|
402
|
+
* Creates the force directed chart links.
|
|
403
|
+
* @param svg the svg element
|
|
404
|
+
* @param config the chart configuration
|
|
405
|
+
* @param data the chart data
|
|
406
|
+
* @returns the chart links
|
|
407
|
+
*/
|
|
408
|
+
const createLinks = (svg, config, data) => {
|
|
409
|
+
return svg
|
|
410
|
+
.selectAll('.link')
|
|
411
|
+
.data(data.links)
|
|
412
|
+
.enter()
|
|
413
|
+
.append('line')
|
|
414
|
+
.attr('class', 'link')
|
|
415
|
+
.style('stroke', '#000000')
|
|
416
|
+
.style('stroke-width', config.strokeWidth);
|
|
417
|
+
};
|
|
418
|
+
/**
|
|
419
|
+
* Creates the force directed chart forces.
|
|
420
|
+
* @param config the chart configuration
|
|
421
|
+
* @param data the chart data
|
|
422
|
+
* @returns the chart forces
|
|
423
|
+
*/
|
|
424
|
+
const createForces = (config, data) => {
|
|
425
|
+
return d3
|
|
426
|
+
.forceSimulation(data.nodes)
|
|
427
|
+
.force('link', d3.forceLink().id(d => d.index ?? 0))
|
|
428
|
+
.force('charge', d3.forceManyBody().strength(config.charge.strength).theta(config.charge.theta).distanceMax(config.charge.distanceMax))
|
|
429
|
+
.force('center', d3.forceCenter(config.width / config.centerCalcMod, config.height / config.centerCalcMod))
|
|
430
|
+
.force('collision', d3.forceCollide().radius(d => config.collisionRadius))
|
|
431
|
+
.force('link', d3
|
|
432
|
+
.forceLink(data.links)
|
|
433
|
+
.id(d => d.index ?? 0)
|
|
434
|
+
.distance(config.distance)
|
|
435
|
+
.links(data.links));
|
|
436
|
+
};
|
|
437
|
+
/**
|
|
438
|
+
* The force directed chart node drag start handler.
|
|
439
|
+
* @param event a drag event
|
|
440
|
+
* @param datum the chart data
|
|
441
|
+
* @param force the chart forces
|
|
442
|
+
*/
|
|
443
|
+
const nodeDragStartHandler = (event, datum, force) => {
|
|
444
|
+
if (!event.active && typeof force !== 'undefined') {
|
|
445
|
+
const alphaTarget = 0.3;
|
|
446
|
+
force.alphaTarget(alphaTarget).restart();
|
|
447
|
+
}
|
|
448
|
+
datum.fx = event.x;
|
|
449
|
+
datum.fy = event.y;
|
|
450
|
+
};
|
|
451
|
+
/**
|
|
452
|
+
* The force directed chart node drag handler.
|
|
453
|
+
* @param event a drag event
|
|
454
|
+
* @param datum the chart data
|
|
455
|
+
* @param config the chart configuration
|
|
456
|
+
*/
|
|
457
|
+
const nodeDragHandler = (event, datum, config) => {
|
|
458
|
+
datum.fx = event.x > config.margin.left && event.x < config.width + config.margin.right ? event.x : datum.fx;
|
|
459
|
+
datum.fy = event.y > config.margin.top && event.y < config.width + config.margin.bottom ? event.y : datum.fy;
|
|
460
|
+
};
|
|
461
|
+
/**
|
|
462
|
+
* The force directed chart node drag end handler.
|
|
463
|
+
* @param event a drag event
|
|
464
|
+
* @param datum the chart data
|
|
465
|
+
* @param force the chart forces
|
|
466
|
+
*/
|
|
467
|
+
const nodeDragEndHandler = (event, datum, force) => {
|
|
468
|
+
if (!event.active && typeof force !== 'undefined') {
|
|
469
|
+
force.alphaTarget(0);
|
|
470
|
+
}
|
|
471
|
+
datum.fx = null;
|
|
472
|
+
datum.fy = null;
|
|
473
|
+
};
|
|
474
|
+
/**
|
|
475
|
+
* Creates the force directed chart nodes.
|
|
476
|
+
* @param svg the svg element
|
|
477
|
+
* @param data the chart data
|
|
478
|
+
* @param force the chart forces
|
|
479
|
+
* @param config the chart configuration
|
|
480
|
+
* @returns the chart nodes
|
|
481
|
+
*/
|
|
482
|
+
const createNodes = (svg, data, force, config) => {
|
|
483
|
+
return svg
|
|
484
|
+
.selectAll('.node')
|
|
485
|
+
.data(data.nodes)
|
|
486
|
+
.enter()
|
|
487
|
+
.append('circle')
|
|
488
|
+
.attr('class', 'node')
|
|
489
|
+
.attr('r', val => {
|
|
490
|
+
const base = 5;
|
|
491
|
+
return base + (val.value ?? 0) + (val.linksCount ?? 0) * 2;
|
|
492
|
+
})
|
|
493
|
+
.style('stroke-width', val => {
|
|
494
|
+
const base = 5;
|
|
495
|
+
return base + (val.value ?? 0) + (val.linksCount ?? 0) * 2;
|
|
496
|
+
})
|
|
497
|
+
.style('fill', val => (typeof val.img === 'undefined' || val.img === '' ? '#f00000' : `url(${val.img})`))
|
|
498
|
+
.call(d3
|
|
499
|
+
.drag()
|
|
500
|
+
.on('start', function (event, datum) {
|
|
501
|
+
nodeDragStartHandler(event, datum, force);
|
|
502
|
+
})
|
|
503
|
+
.on('drag', function (event, datum) {
|
|
504
|
+
nodeDragHandler(event, datum, config);
|
|
505
|
+
})
|
|
506
|
+
.on('end', function (event, datum) {
|
|
507
|
+
nodeDragEndHandler(event, datum, force);
|
|
508
|
+
}));
|
|
509
|
+
};
|
|
510
|
+
/**
|
|
511
|
+
* Creates the force directed chart text labels.
|
|
512
|
+
* @param svg the svg element
|
|
513
|
+
* @param data the chart data
|
|
514
|
+
* @returns the chart text labels
|
|
515
|
+
*/
|
|
516
|
+
const createText = (svg, data) => {
|
|
517
|
+
return svg
|
|
518
|
+
.append('g')
|
|
519
|
+
.selectAll('text')
|
|
520
|
+
.data(data.nodes)
|
|
521
|
+
.enter()
|
|
522
|
+
.append('text')
|
|
523
|
+
.attr('class', 'legend')
|
|
524
|
+
.text(val => val.name ?? `N/A (id. ${val.index})`);
|
|
525
|
+
};
|
|
526
|
+
/**
|
|
527
|
+
* Draws the force directed chart.
|
|
528
|
+
* @param container the chart container
|
|
529
|
+
* @param data the chart data
|
|
530
|
+
* @param options the chart options
|
|
531
|
+
* @returns the chart configuration
|
|
532
|
+
*/
|
|
533
|
+
const drawForceDirectedChart = (container, data, options) => {
|
|
534
|
+
const config = generateConfiguration(defaultForceDirectedChartConfig, options, {});
|
|
535
|
+
const { svg, g } = createContainer$3(container, config);
|
|
536
|
+
applyChartData(g, data);
|
|
537
|
+
const link = createLinks(svg, config, data);
|
|
538
|
+
const force = createForces(config, data);
|
|
539
|
+
const node = createNodes(svg, data, force, config);
|
|
540
|
+
const text = createText(svg, data);
|
|
541
|
+
force.on('tick', () => {
|
|
542
|
+
ticked(link, node, text);
|
|
543
|
+
});
|
|
544
|
+
return config;
|
|
545
|
+
};
|
|
546
|
+
|
|
547
|
+
/**
|
|
548
|
+
* The line chart default configuration.
|
|
549
|
+
*/
|
|
550
|
+
const defaultLineChartConfig = Object.freeze({
|
|
551
|
+
chartTitle: '',
|
|
552
|
+
width: 350,
|
|
553
|
+
height: 350,
|
|
554
|
+
margin: {
|
|
555
|
+
top: 70,
|
|
556
|
+
right: 50,
|
|
557
|
+
bottom: 50,
|
|
558
|
+
left: 50,
|
|
559
|
+
},
|
|
560
|
+
transitionDuration: 400,
|
|
561
|
+
dotRadius: 3.5,
|
|
562
|
+
xAxisTitle: '',
|
|
563
|
+
yAxisTitle: '',
|
|
564
|
+
ticks: {
|
|
565
|
+
x: 5,
|
|
566
|
+
y: 10,
|
|
567
|
+
},
|
|
568
|
+
displayAxisLabels: true,
|
|
569
|
+
labelTextWrapWidth: 20,
|
|
570
|
+
color: d3.scaleOrdinal(d3.schemeCategory10),
|
|
571
|
+
});
|
|
572
|
+
/**
|
|
573
|
+
* Creates a container for the line chart.
|
|
574
|
+
* @param container the chart container
|
|
575
|
+
* @param config the chart configuration
|
|
576
|
+
* @returns the object with the svg element and the g element
|
|
577
|
+
*/
|
|
578
|
+
const createContainer$2 = (container, config) => {
|
|
579
|
+
const id = container.nativeElement.id ?? 'line-0';
|
|
580
|
+
d3.select(`#${id}`).select('svg').remove();
|
|
581
|
+
const svg = d3
|
|
582
|
+
.select(`#${id}`)
|
|
583
|
+
.append('svg')
|
|
584
|
+
.attr('width', config.width + config.margin.left + config.margin.right)
|
|
585
|
+
.attr('height', config.height + config.margin.top + config.margin.bottom)
|
|
586
|
+
.attr('class', id);
|
|
587
|
+
const g = svg.append('g').attr('transform', `translate(${config.margin.left},${config.margin.top / 2})`);
|
|
588
|
+
return { svg, g };
|
|
589
|
+
};
|
|
590
|
+
/**
|
|
591
|
+
* Wraps the line chart axis labels text.
|
|
592
|
+
* @param svgText the svg text elements
|
|
593
|
+
* @param width the chart axis label width
|
|
594
|
+
*/
|
|
595
|
+
const wrapSvgText$1 = (svgText, width) => {
|
|
596
|
+
svgText.each(function () {
|
|
597
|
+
const text = d3.select(this);
|
|
598
|
+
const words = text.text().split(/\s+/).reverse();
|
|
599
|
+
let line = [];
|
|
600
|
+
let lineNumber = 0;
|
|
601
|
+
const lineHeight = 1.4;
|
|
602
|
+
const y = text.attr('y');
|
|
603
|
+
const x = text.attr('x');
|
|
604
|
+
const dy = parseFloat(text.attr('dy') ?? 0);
|
|
605
|
+
let tspan = text.text(null).append('tspan').attr('x', x).attr('y', y).attr('dy', `${dy}em`); // axis label
|
|
606
|
+
let word = words.pop();
|
|
607
|
+
while (typeof word !== 'undefined') {
|
|
608
|
+
line.push(word ?? '');
|
|
609
|
+
tspan.text(line.join(' '));
|
|
610
|
+
if ((tspan.node()?.getComputedTextLength() ?? 0) > width) {
|
|
611
|
+
line.pop();
|
|
612
|
+
tspan.text(line.join(' '));
|
|
613
|
+
line = [word ?? ''];
|
|
614
|
+
lineNumber += 1;
|
|
615
|
+
tspan = text
|
|
616
|
+
.append('tspan')
|
|
617
|
+
.attr('x', 0)
|
|
618
|
+
.attr('y', y)
|
|
619
|
+
.attr('dy', `${lineNumber * lineHeight + dy}em`)
|
|
620
|
+
.text(word ?? '');
|
|
621
|
+
}
|
|
622
|
+
word = words.pop();
|
|
623
|
+
}
|
|
624
|
+
});
|
|
625
|
+
};
|
|
626
|
+
/**
|
|
627
|
+
* Creates the legend.
|
|
628
|
+
* @param g the svg g element
|
|
629
|
+
* @param config the chart configuration
|
|
630
|
+
*/
|
|
631
|
+
const createLegend$1 = (g, config) => {
|
|
632
|
+
if (config.displayAxisLabels && config.xAxisTitle !== '') {
|
|
633
|
+
g.append('g')
|
|
634
|
+
.attr('transform', `translate(0, ${config.height + config.margin.bottom})`)
|
|
635
|
+
.append('text')
|
|
636
|
+
.style('font-size', '12px')
|
|
637
|
+
.attr('class', 'legend')
|
|
638
|
+
.attr('dx', '1.5em')
|
|
639
|
+
.attr('dy', '1em')
|
|
640
|
+
.text(`x - ${config.xAxisTitle}`);
|
|
641
|
+
}
|
|
642
|
+
if (config.displayAxisLabels && config.yAxisTitle !== '') {
|
|
643
|
+
g.append('g')
|
|
644
|
+
.attr('transform', `translate(0, ${config.height + config.margin.bottom})`)
|
|
645
|
+
.append('text')
|
|
646
|
+
.style('font-size', '12px')
|
|
647
|
+
.attr('class', 'legend')
|
|
648
|
+
.attr('dx', '1.5em')
|
|
649
|
+
.attr('dy', '2.5em')
|
|
650
|
+
.text(`y - ${config.yAxisTitle}`);
|
|
651
|
+
}
|
|
652
|
+
if (config.chartTitle !== '') {
|
|
653
|
+
g.append('g')
|
|
654
|
+
.attr('transform', `translate(0, 0)`)
|
|
655
|
+
.append('text')
|
|
656
|
+
.style('font-size', '12px')
|
|
657
|
+
.attr('class', 'legend')
|
|
658
|
+
.attr('dx', '1.5em')
|
|
659
|
+
.attr('dy', '-2em')
|
|
660
|
+
.text(config.chartTitle);
|
|
661
|
+
}
|
|
662
|
+
};
|
|
663
|
+
/**
|
|
664
|
+
* Creates the x axis.
|
|
665
|
+
* @param g the svg g element
|
|
666
|
+
* @param x the x axis scale
|
|
667
|
+
* @param config the chart configuration
|
|
668
|
+
*/
|
|
669
|
+
const createAxisX = (g, x, config) => {
|
|
670
|
+
const xLabels = g
|
|
671
|
+
.append('g')
|
|
672
|
+
.attr('transform', `translate(0, ${config.height})`)
|
|
673
|
+
.call(d3
|
|
674
|
+
.axisBottom(x)
|
|
675
|
+
.ticks(config.ticks.x)
|
|
676
|
+
.tickFormat(d => {
|
|
677
|
+
const date = new Date(d.valueOf());
|
|
678
|
+
const formattingOffset = 10;
|
|
679
|
+
const day = date.getDate();
|
|
680
|
+
const dd = day < formattingOffset ? `0${day}` : day;
|
|
681
|
+
const month = date.getMonth() + 1;
|
|
682
|
+
const mm = month < formattingOffset ? `0${month}` : month;
|
|
683
|
+
const year = date.getFullYear().toString();
|
|
684
|
+
const yy = year.slice(2);
|
|
685
|
+
const hours = date.getHours();
|
|
686
|
+
const hour = hours < formattingOffset ? `0${hours}` : hours;
|
|
687
|
+
const minutes = date.getMinutes();
|
|
688
|
+
const minute = minutes < formattingOffset ? `0${minutes}` : minutes;
|
|
689
|
+
return `${dd}/${mm}/${yy} ${hour}:${minute}`;
|
|
690
|
+
}))
|
|
691
|
+
.append('text');
|
|
692
|
+
g.selectAll('text').call(wrapSvgText$1, config.labelTextWrapWidth);
|
|
693
|
+
if (config.displayAxisLabels) {
|
|
694
|
+
xLabels.attr('transform', `translate(${config.width}, 0)`).attr('class', 'legend').attr('dx', '1.5em').attr('dy', '0.7em').text('x');
|
|
695
|
+
}
|
|
696
|
+
};
|
|
697
|
+
/**
|
|
698
|
+
* Creates the y axis.
|
|
699
|
+
* @param g the svg g element
|
|
700
|
+
* @param y the y axis scale
|
|
701
|
+
* @param config the chart configuration
|
|
702
|
+
*/
|
|
703
|
+
const createAxisY = (g, y, config) => {
|
|
704
|
+
const yLabels = g
|
|
705
|
+
.append('g')
|
|
706
|
+
.call(d3
|
|
707
|
+
.axisLeft(y)
|
|
708
|
+
.ticks(config.ticks.y)
|
|
709
|
+
.tickFormat(d => `${d}`))
|
|
710
|
+
.append('text');
|
|
711
|
+
if (config.displayAxisLabels) {
|
|
712
|
+
yLabels.attr('class', 'legend').attr('dy', '-1.5em').attr('class', 'legend').text('y');
|
|
713
|
+
}
|
|
714
|
+
};
|
|
715
|
+
/**
|
|
716
|
+
* The mouse over event handler.
|
|
717
|
+
* @param self an svg circle element
|
|
718
|
+
* @param d the chart data node
|
|
719
|
+
* @param g the svg g element
|
|
720
|
+
* @param config the chart configuration
|
|
721
|
+
*/
|
|
722
|
+
const onMouseOver = (self, d, g, config) => {
|
|
723
|
+
const duration = 400;
|
|
724
|
+
d3.select(self)
|
|
725
|
+
.transition()
|
|
726
|
+
.duration(duration)
|
|
727
|
+
.attr('r', config.dotRadius * 2);
|
|
728
|
+
const tooltipShift = 4;
|
|
729
|
+
const tooltipDy = -10;
|
|
730
|
+
g.append('text')
|
|
731
|
+
.attr('class', 'val')
|
|
732
|
+
.style('font-size', '11px')
|
|
733
|
+
.attr('dx', () => (config.width - config.margin.left - config.margin.right) / tooltipShift)
|
|
734
|
+
.attr('dy', () => tooltipDy)
|
|
735
|
+
.text(() => `${d.value} (${new Date(d.timestamp).toUTCString()})`);
|
|
736
|
+
};
|
|
737
|
+
/**
|
|
738
|
+
* The mouse out event handler.
|
|
739
|
+
* @param self an svg circle element
|
|
740
|
+
* @param config the chart configuration
|
|
741
|
+
*/
|
|
742
|
+
const onMouseOut = (self, config) => {
|
|
743
|
+
const duration = 400;
|
|
744
|
+
d3.select(self).attr('class', 'dot');
|
|
745
|
+
d3.select(self).transition().duration(duration).attr('r', config.dotRadius);
|
|
746
|
+
d3.selectAll('.val').remove();
|
|
747
|
+
};
|
|
748
|
+
/**
|
|
749
|
+
* Draws the chart lines, dots, and sets the mouse pointer events.
|
|
750
|
+
* @param g the svg g element
|
|
751
|
+
* @param x the x axis scale
|
|
752
|
+
* @param y the y axis scale
|
|
753
|
+
* @param config the chart configuration
|
|
754
|
+
* @param data the chart data
|
|
755
|
+
*/
|
|
756
|
+
const drawLinesDotsAndSetPointerEvents = (g, x, y, config, data) => {
|
|
757
|
+
const line = d3
|
|
758
|
+
.line()
|
|
759
|
+
.x(d => x(d.timestamp))
|
|
760
|
+
.y(d => y(d.value))
|
|
761
|
+
.curve(d3.curveMonotoneX);
|
|
762
|
+
g.append('path').attr('id', 'line').style('fill', 'none').style('stroke', 'red').style('stroke-width', '2px').attr('d', line(data));
|
|
763
|
+
g.selectAll('.dot')
|
|
764
|
+
.data(data)
|
|
765
|
+
.enter()
|
|
766
|
+
.append('circle')
|
|
767
|
+
.attr('class', 'dot')
|
|
768
|
+
.style('pointer-events', 'all')
|
|
769
|
+
.style('fill', (d, i) => config.color(i.toString()))
|
|
770
|
+
.on('mouseover', function (event, d) {
|
|
771
|
+
return onMouseOver(this, d, g, config);
|
|
772
|
+
})
|
|
773
|
+
.on('mouseout', function () {
|
|
774
|
+
return onMouseOut(this, config);
|
|
775
|
+
})
|
|
776
|
+
.attr('cx', function (d) {
|
|
777
|
+
return x(d.timestamp);
|
|
778
|
+
})
|
|
779
|
+
.attr('cy', function (d) {
|
|
780
|
+
return y(d.value);
|
|
781
|
+
})
|
|
782
|
+
.attr('r', 0)
|
|
783
|
+
.transition()
|
|
784
|
+
.ease(d3.easeLinear)
|
|
785
|
+
.duration(config.transitionDuration)
|
|
786
|
+
.delay((d, i) => {
|
|
787
|
+
const multiplier = 50;
|
|
788
|
+
return i * multiplier;
|
|
789
|
+
})
|
|
790
|
+
.attr('r', config.dotRadius);
|
|
791
|
+
};
|
|
792
|
+
/**
|
|
793
|
+
* Draws the line chart.
|
|
794
|
+
* @param container the chart container
|
|
795
|
+
* @param data the chart data
|
|
796
|
+
* @param options the chart options
|
|
797
|
+
* @returns the chart configuration
|
|
798
|
+
*/
|
|
799
|
+
const drawLineChart = (container, data, options) => {
|
|
800
|
+
const config = generateConfiguration(defaultLineChartConfig, options, {});
|
|
801
|
+
const { g } = createContainer$2(container, config);
|
|
802
|
+
const x = d3.scaleTime([0, config.width]).domain([Math.min(...data.map(d => d.timestamp)), Math.max(...data.map(d => d.timestamp))]);
|
|
803
|
+
const y = d3.scaleLinear([config.height, 0]).domain([0, d3.max(data, d => d.value) ?? 1]);
|
|
804
|
+
createAxisX(g, x, config);
|
|
805
|
+
createAxisY(g, y, config);
|
|
806
|
+
createLegend$1(g, config);
|
|
807
|
+
drawLinesDotsAndSetPointerEvents(g, x, y, config, data);
|
|
808
|
+
return config;
|
|
809
|
+
};
|
|
810
|
+
|
|
811
|
+
/**
|
|
812
|
+
* The pie chart default configuration.
|
|
813
|
+
*/
|
|
814
|
+
const defaultPieChartConfig = Object.freeze({
|
|
815
|
+
chartTitle: '',
|
|
816
|
+
width: 600,
|
|
817
|
+
height: 600,
|
|
818
|
+
margin: {
|
|
819
|
+
top: 20,
|
|
820
|
+
right: 20,
|
|
821
|
+
bottom: 20,
|
|
822
|
+
left: 20,
|
|
823
|
+
},
|
|
824
|
+
innerRadius: 0,
|
|
825
|
+
labelRadiusModifier: 50,
|
|
826
|
+
labelTextWrapWidth: 60,
|
|
827
|
+
color: d3.scaleOrdinal(d3.schemeCategory10),
|
|
828
|
+
});
|
|
829
|
+
/**
|
|
830
|
+
* Creates a container for the pie chart.
|
|
831
|
+
* @param container the chart container
|
|
832
|
+
* @param config the chart configuration
|
|
833
|
+
* @returns the object with the svg element and the g element
|
|
834
|
+
*/
|
|
835
|
+
const createContainer$1 = (container, config) => {
|
|
836
|
+
const id = container.nativeElement.id ?? 'pie-0';
|
|
837
|
+
d3.select(`#${id}`).select('svg').remove();
|
|
838
|
+
const svg = d3
|
|
839
|
+
.select(`#${id}`)
|
|
840
|
+
.append('svg')
|
|
841
|
+
.attr('width', config.width + config.margin.left + config.margin.right)
|
|
842
|
+
.attr('height', config.height + config.margin.top + config.margin.bottom)
|
|
843
|
+
.attr('class', id);
|
|
844
|
+
const g = svg
|
|
845
|
+
.append('g')
|
|
846
|
+
.attr('transform', `translate(${config.width / 2 + config.margin.left},${config.height / 2 + config.margin.top})`);
|
|
847
|
+
return { svg, g };
|
|
848
|
+
};
|
|
849
|
+
/**
|
|
850
|
+
* Draws the pie chart.
|
|
851
|
+
* @param container the chart container
|
|
852
|
+
* @param data the chart data
|
|
853
|
+
* @param options the chart options
|
|
854
|
+
* @returns the chart configuration
|
|
855
|
+
*/
|
|
856
|
+
const drawPieChart = (container, data, options) => {
|
|
857
|
+
const config = generateConfiguration(defaultPieChartConfig, options, {});
|
|
858
|
+
const { g } = createContainer$1(container, config);
|
|
859
|
+
const pie = d3.pie().value(datum => datum.y);
|
|
860
|
+
const radius = Math.min(config.width, config.height) / 2;
|
|
861
|
+
const arc = d3.arc().innerRadius(config.innerRadius).outerRadius(radius);
|
|
862
|
+
const arcs = g
|
|
863
|
+
.selectAll('arc')
|
|
864
|
+
.data(pie(data))
|
|
865
|
+
.enter()
|
|
866
|
+
.append('g')
|
|
867
|
+
.attr('class', 'arc')
|
|
868
|
+
.on('mouseover', function (event, d) {
|
|
869
|
+
d3.select('#tooltip')
|
|
870
|
+
.style('left', `${event.pageX}px`)
|
|
871
|
+
.style('top', `${event.pageY}px`)
|
|
872
|
+
.style('opacity', 1)
|
|
873
|
+
.select('#value')
|
|
874
|
+
.text(d.value);
|
|
875
|
+
})
|
|
876
|
+
.on('mouseout', function (event, d) {
|
|
877
|
+
d3.select('#tooltip').style('opacity', 0);
|
|
878
|
+
});
|
|
879
|
+
const label = d3
|
|
880
|
+
.arc()
|
|
881
|
+
.innerRadius(radius)
|
|
882
|
+
.outerRadius(radius + config.labelRadiusModifier);
|
|
883
|
+
arcs
|
|
884
|
+
.append('path')
|
|
885
|
+
.attr('fill', (d, i) => config.color(i.toString()))
|
|
886
|
+
.attr('d', arc);
|
|
887
|
+
const textDy = 5;
|
|
888
|
+
arcs
|
|
889
|
+
.append('text')
|
|
890
|
+
.attr('class', 'legend')
|
|
891
|
+
.attr('text-anchor', 'middle')
|
|
892
|
+
.attr('dy', textDy)
|
|
893
|
+
.attr('transform', d => `translate(${label.centroid(d)})`)
|
|
894
|
+
.text(d => d.data.y);
|
|
895
|
+
return config;
|
|
896
|
+
};
|
|
897
|
+
|
|
898
|
+
/**
|
|
899
|
+
* The radar chart default configuration.
|
|
900
|
+
*/
|
|
901
|
+
const defaultRadarChartConfig = Object.freeze({
|
|
902
|
+
chartTitle: '',
|
|
903
|
+
width: 350,
|
|
904
|
+
height: 350,
|
|
905
|
+
margin: {
|
|
906
|
+
top: 50,
|
|
907
|
+
right: 50,
|
|
908
|
+
bottom: 50,
|
|
909
|
+
left: 50,
|
|
910
|
+
},
|
|
911
|
+
levels: 3,
|
|
912
|
+
maxValue: 0,
|
|
913
|
+
lineFactor: 1.1,
|
|
914
|
+
labelFactor: 1.15,
|
|
915
|
+
labelTextWrapWidth: 60,
|
|
916
|
+
opacityArea: 0.35,
|
|
917
|
+
dotRadius: 4,
|
|
918
|
+
opacityCircles: 0.1,
|
|
919
|
+
strokeWidth: 2,
|
|
920
|
+
roundStrokes: false,
|
|
921
|
+
transitionDuration: 200,
|
|
922
|
+
color: d3.scaleOrdinal(d3.schemeCategory10),
|
|
923
|
+
});
|
|
924
|
+
/**
|
|
925
|
+
* Creates a container for the radar chart.
|
|
926
|
+
* @param container the chart container
|
|
927
|
+
* @param config the chart configuration
|
|
928
|
+
* @returns the object with the svg element and the g element
|
|
929
|
+
*/
|
|
930
|
+
const createContainer = (container, config) => {
|
|
931
|
+
const id = container.nativeElement.id ?? 'radar-0';
|
|
932
|
+
d3.select(`#${id}`).select('svg').remove();
|
|
933
|
+
const svg = d3
|
|
934
|
+
.select(`#${id}`)
|
|
935
|
+
.append('svg')
|
|
936
|
+
.attr('width', config.width + config.margin.left + config.margin.right)
|
|
937
|
+
.attr('height', config.height + config.margin.top + config.margin.bottom)
|
|
938
|
+
.attr('class', id);
|
|
939
|
+
const g = svg
|
|
940
|
+
.append('g')
|
|
941
|
+
.attr('transform', `translate(${config.width / 2 + config.margin.left},${config.height / 2 + config.margin.top})`);
|
|
942
|
+
return { svg, g };
|
|
943
|
+
};
|
|
944
|
+
/**
|
|
945
|
+
* Draws the radar chart circular grid.
|
|
946
|
+
* @param axisGrid the chart axis grid
|
|
947
|
+
* @param radius the chart radius value
|
|
948
|
+
* @param maxValue the maximum value of the chart axis
|
|
949
|
+
* @param config the chart configuration
|
|
950
|
+
*/
|
|
951
|
+
const drawCircularGrid = (axisGrid, radius, maxValue, config) => {
|
|
952
|
+
// background circles
|
|
953
|
+
axisGrid
|
|
954
|
+
.selectAll('.levels')
|
|
955
|
+
.data(d3.range(1, config.levels + 1).reverse())
|
|
956
|
+
.enter()
|
|
957
|
+
.append('circle')
|
|
958
|
+
.attr('class', 'grid-circle')
|
|
959
|
+
.attr('r', (d, i) => (radius / config.levels) * d)
|
|
960
|
+
.style('fill', '#CDCDCD')
|
|
961
|
+
.style('stroke', '#CDCDCD')
|
|
962
|
+
.style('fill-opacity', config.opacityCircles)
|
|
963
|
+
.style('filter', 'url(#glow)');
|
|
964
|
+
// text indicating at what % each level is
|
|
965
|
+
const axisGridX = 4;
|
|
966
|
+
axisGrid
|
|
967
|
+
.selectAll('.axis-label')
|
|
968
|
+
.data(d3.range(1, config.levels + 1).reverse())
|
|
969
|
+
.enter()
|
|
970
|
+
.append('text')
|
|
971
|
+
.attr('class', 'axis-label')
|
|
972
|
+
.attr('x', axisGridX)
|
|
973
|
+
.attr('y', d => (-d * radius) / config.levels)
|
|
974
|
+
.attr('dy', '0.4em')
|
|
975
|
+
.style('font-size', '10px')
|
|
976
|
+
.attr('fill', '#737373')
|
|
977
|
+
.text((d, i) => (maxValue * d) / config.levels);
|
|
978
|
+
};
|
|
979
|
+
/**
|
|
980
|
+
* Wraps the chart axis labels text.
|
|
981
|
+
* @param svgText the svg text elements
|
|
982
|
+
* @param width the chart axis label width
|
|
983
|
+
*/
|
|
984
|
+
const wrapSvgText = (svgText, width) => {
|
|
985
|
+
svgText.each(function () {
|
|
986
|
+
const text = d3.select(this);
|
|
987
|
+
const words = text.text().split(/\s+/).reverse();
|
|
988
|
+
let line = [];
|
|
989
|
+
let lineNumber = 0;
|
|
990
|
+
const lineHeight = 1.4;
|
|
991
|
+
const y = text.attr('y');
|
|
992
|
+
const x = text.attr('x');
|
|
993
|
+
const dy = parseFloat(text.attr('dy') ?? 0);
|
|
994
|
+
let tspan = text.text(null).append('tspan').attr('x', x).attr('y', y).attr('dy', `${dy}em`);
|
|
995
|
+
let word = words.pop();
|
|
996
|
+
while (typeof word !== 'undefined') {
|
|
997
|
+
line.push(word ?? '');
|
|
998
|
+
tspan.text(line.join(' '));
|
|
999
|
+
if ((tspan.node()?.getComputedTextLength() ?? 0) > width) {
|
|
1000
|
+
line.pop();
|
|
1001
|
+
tspan.text(line.join(' '));
|
|
1002
|
+
line = [word ?? ''];
|
|
1003
|
+
lineNumber += 1;
|
|
1004
|
+
tspan = text
|
|
1005
|
+
.append('tspan')
|
|
1006
|
+
.attr('x', x)
|
|
1007
|
+
.attr('y', y)
|
|
1008
|
+
.attr('dy', `${lineNumber * lineHeight + dy}em`)
|
|
1009
|
+
.text(word ?? '');
|
|
1010
|
+
}
|
|
1011
|
+
word = words.pop();
|
|
1012
|
+
}
|
|
1013
|
+
});
|
|
1014
|
+
};
|
|
1015
|
+
/**
|
|
1016
|
+
* Creates the legend.
|
|
1017
|
+
* @param g the svg g element
|
|
1018
|
+
* @param config the chart configuration
|
|
1019
|
+
*/
|
|
1020
|
+
const createLegend = (g, config) => {
|
|
1021
|
+
if (config.chartTitle !== '') {
|
|
1022
|
+
g.append('g')
|
|
1023
|
+
.attr('transform', `translate(-${config.width / 2 + config.margin.left / 2}, -${config.height / 2 + config.margin.top / 2})`)
|
|
1024
|
+
.append('text')
|
|
1025
|
+
.style('font-size', '12px')
|
|
1026
|
+
.attr('class', 'legend')
|
|
1027
|
+
.text(config.chartTitle);
|
|
1028
|
+
}
|
|
1029
|
+
};
|
|
1030
|
+
/**
|
|
1031
|
+
* Draws the radar chart axis.
|
|
1032
|
+
* @param axisGrid the chart axis grid
|
|
1033
|
+
* @param axisNames the chart axis names
|
|
1034
|
+
* @param radiusScale the chart radius scale
|
|
1035
|
+
* @param maxValue the maximum value of the chart axis
|
|
1036
|
+
* @param angleSlice the chart angle slice value
|
|
1037
|
+
* @param config the chart configuration
|
|
1038
|
+
*/
|
|
1039
|
+
const drawAxis = (axisGrid, axisNames, radiusScale, maxValue, angleSlice, config) => {
|
|
1040
|
+
// create the straight lines radiating outward from the center
|
|
1041
|
+
const axis = axisGrid.selectAll('.axis').data(axisNames).enter().append('g').attr('class', 'axis');
|
|
1042
|
+
// append the lines
|
|
1043
|
+
axis
|
|
1044
|
+
.append('line')
|
|
1045
|
+
.attr('x1', 0)
|
|
1046
|
+
.attr('y1', 0)
|
|
1047
|
+
.attr('x2', (d, i) => radiusScale(maxValue * config.lineFactor) * Math.cos(angleSlice * i - Math.PI / 2))
|
|
1048
|
+
.attr('y2', (d, i) => radiusScale(maxValue * config.lineFactor) * Math.sin(angleSlice * i - Math.PI / 2))
|
|
1049
|
+
.attr('class', 'line')
|
|
1050
|
+
.style('stroke', 'white')
|
|
1051
|
+
.style('stroke-width', '2px');
|
|
1052
|
+
// append the labels at each axis
|
|
1053
|
+
axis
|
|
1054
|
+
.append('text')
|
|
1055
|
+
.attr('class', 'legend')
|
|
1056
|
+
.style('font-size', '11px')
|
|
1057
|
+
.attr('text-anchor', 'middle')
|
|
1058
|
+
.attr('dy', '0.35em')
|
|
1059
|
+
.attr('x', (d, i) => radiusScale(maxValue * config.labelFactor) * Math.cos(angleSlice * i - Math.PI / 2))
|
|
1060
|
+
.attr('y', (d, i) => radiusScale(maxValue * config.labelFactor) * Math.sin(angleSlice * i - Math.PI / 2))
|
|
1061
|
+
.text(d => d)
|
|
1062
|
+
.call(wrapSvgText, config.labelTextWrapWidth);
|
|
1063
|
+
};
|
|
1064
|
+
/**
|
|
1065
|
+
* Draws the radar chart blobs.
|
|
1066
|
+
* @param radiusScale the chart radius scale
|
|
1067
|
+
* @param angleSlice the chart angle slice value
|
|
1068
|
+
* @param g the svg g element
|
|
1069
|
+
* @param data the chart data
|
|
1070
|
+
* @param config the chart configuration
|
|
1071
|
+
*/
|
|
1072
|
+
const drawRadarChartBlobs = (radiusScale, angleSlice, g, data, config) => {
|
|
1073
|
+
// the radial line function
|
|
1074
|
+
const radarLine = d3
|
|
1075
|
+
.lineRadial()
|
|
1076
|
+
.radius(d => radiusScale(d.value))
|
|
1077
|
+
.angle((d, i) => i * angleSlice);
|
|
1078
|
+
// create a wrapper for the blobs
|
|
1079
|
+
const blobWrapper = g.selectAll('.radar-wrapper').data(data).enter().append('g').attr('class', 'radar-wrapper');
|
|
1080
|
+
// append the backgrounds
|
|
1081
|
+
blobWrapper
|
|
1082
|
+
.append('path')
|
|
1083
|
+
.attr('class', 'radar-area')
|
|
1084
|
+
.attr('d', (d, i) => radarLine(d))
|
|
1085
|
+
.style('fill', (d, i) => config.color(i.toString()))
|
|
1086
|
+
.style('fill-opacity', config.opacityArea)
|
|
1087
|
+
.on('mouseover', function (d, i) {
|
|
1088
|
+
// dim all blobs
|
|
1089
|
+
const radarAreaFillOpacity = 0.1;
|
|
1090
|
+
d3.selectAll('.radar-area').transition().duration(config.transitionDuration).style('fill-opacity', radarAreaFillOpacity);
|
|
1091
|
+
// bring back the hovered over blob
|
|
1092
|
+
const fillOpacity = 0.7;
|
|
1093
|
+
d3.select(this).transition().duration(config.transitionDuration).style('fill-opacity', fillOpacity);
|
|
1094
|
+
})
|
|
1095
|
+
.on('mouseout', () => {
|
|
1096
|
+
// bring back all blobs
|
|
1097
|
+
d3.selectAll('.radar-area').transition().duration(config.transitionDuration).style('fill-opacity', config.opacityArea);
|
|
1098
|
+
});
|
|
1099
|
+
// create the outlines
|
|
1100
|
+
blobWrapper
|
|
1101
|
+
.append('path')
|
|
1102
|
+
.attr('class', 'radar-stroke')
|
|
1103
|
+
.attr('d', (d, i) => radarLine(d))
|
|
1104
|
+
.style('stroke-width', `${config.strokeWidth}px`)
|
|
1105
|
+
.style('stroke', (d, i) => config.color(i.toString()))
|
|
1106
|
+
.style('fill', 'none')
|
|
1107
|
+
.style('filter', 'url(#glow)');
|
|
1108
|
+
// append the circles
|
|
1109
|
+
const blobWrapperFillOpacity = 0.8;
|
|
1110
|
+
blobWrapper
|
|
1111
|
+
.selectAll('.radar-circle')
|
|
1112
|
+
.data((d, i) => d)
|
|
1113
|
+
.enter()
|
|
1114
|
+
.append('circle')
|
|
1115
|
+
.attr('class', 'radar-circle')
|
|
1116
|
+
.attr('r', config.dotRadius)
|
|
1117
|
+
.attr('cx', (d, i) => radiusScale(d.value) * Math.cos(angleSlice * i - Math.PI / 2))
|
|
1118
|
+
.attr('cy', (d, i) => radiusScale(d.value) * Math.sin(angleSlice * i - Math.PI / 2))
|
|
1119
|
+
.style('fill', (d, i, j) => config.color(j.toString()))
|
|
1120
|
+
.style('fill-opacity', blobWrapperFillOpacity);
|
|
1121
|
+
};
|
|
1122
|
+
/**
|
|
1123
|
+
* Appends the invisible tooltip circles.
|
|
1124
|
+
* @param g the svg g element
|
|
1125
|
+
* @param data the chart data
|
|
1126
|
+
* @param radiusScale the chart radius scale
|
|
1127
|
+
* @param angleSlice the chart angle slice value
|
|
1128
|
+
* @param config the chart configuration
|
|
1129
|
+
*/
|
|
1130
|
+
const appendInvisibleTooltipCircles = (g, data, radiusScale, angleSlice, config) => {
|
|
1131
|
+
// wrapper for the invisible circles on top
|
|
1132
|
+
const blobCircleWrapper = g.selectAll('.radar-circle-wrapper').data(data).enter().append('g').attr('class', 'radar-circle-wrapper');
|
|
1133
|
+
// set up the small tooltip for when you hover over a circle
|
|
1134
|
+
const tooltip = g.append('text').attr('class', 'tooltip').style('opacity', 0);
|
|
1135
|
+
// append a set of invisible circles on top for the mouseover pop-up
|
|
1136
|
+
const blobCircleWrapperRadiusMultiplier = 1.5;
|
|
1137
|
+
blobCircleWrapper
|
|
1138
|
+
.selectAll('.radar-invisible-circle')
|
|
1139
|
+
.data((d, i) => d)
|
|
1140
|
+
.enter()
|
|
1141
|
+
.append('circle')
|
|
1142
|
+
.attr('class', 'radar-invisible-circle')
|
|
1143
|
+
.attr('r', config.dotRadius * blobCircleWrapperRadiusMultiplier)
|
|
1144
|
+
.attr('cx', (d, i) => radiusScale(d.value) * Math.cos(angleSlice * i - Math.PI / 2))
|
|
1145
|
+
.attr('cy', (d, i) => radiusScale(d.value) * Math.sin(angleSlice * i - Math.PI / 2))
|
|
1146
|
+
.style('fill', 'none')
|
|
1147
|
+
.style('pointer-events', 'all')
|
|
1148
|
+
.on('mouseover', function (event, i) {
|
|
1149
|
+
const modifier = 10;
|
|
1150
|
+
const newX = parseFloat(d3.select(this).attr('cx')) - modifier;
|
|
1151
|
+
const newY = parseFloat(d3.select(this).attr('cy')) - modifier;
|
|
1152
|
+
const nodeData = event.target['__data__'];
|
|
1153
|
+
const tooltipText = `${nodeData.value} ${nodeData.unit}`;
|
|
1154
|
+
tooltip.attr('x', newX).attr('y', newY).text(tooltipText).transition().duration(config.transitionDuration).style('opacity', 1);
|
|
1155
|
+
})
|
|
1156
|
+
.on('mouseout', () => {
|
|
1157
|
+
tooltip.transition().duration(config.transitionDuration).style('opacity', 0);
|
|
1158
|
+
});
|
|
1159
|
+
};
|
|
1160
|
+
/**
|
|
1161
|
+
* Draws the radar chart.
|
|
1162
|
+
* @param container the chart container
|
|
1163
|
+
* @param data the chart data
|
|
1164
|
+
* @param options the chart options
|
|
1165
|
+
* @returns the hart configuration
|
|
1166
|
+
*/
|
|
1167
|
+
const drawRadarChart = (container, data, options) => {
|
|
1168
|
+
const config = generateConfiguration(defaultRadarChartConfig, options, {});
|
|
1169
|
+
const maxValue = Math.max(config.maxValue, d3.max(data, i => d3.max(i.map(o => o.value))) ?? 0);
|
|
1170
|
+
const axisNames = data[0].map((i, j) => i.axis);
|
|
1171
|
+
const totalAxis = axisNames.length;
|
|
1172
|
+
const radius = Math.min(config.width / 2 - config.margin.left / 2, config.height / 2 - config.margin.top / 2);
|
|
1173
|
+
const angleSlice = (Math.PI * 2) / totalAxis;
|
|
1174
|
+
const radiusScale = d3.scaleLinear([0, radius]).domain([0, maxValue]);
|
|
1175
|
+
const { g } = createContainer(container, config);
|
|
1176
|
+
// filter for the outside glow
|
|
1177
|
+
const filter = g.append('defs').append('filter').attr('id', 'glow');
|
|
1178
|
+
filter.append('feGaussianBlur').attr('stdDeviation', '2.5').attr('result', 'coloredBlur');
|
|
1179
|
+
const feMerge = filter.append('feMerge');
|
|
1180
|
+
feMerge.append('feMergeNode').attr('in', 'coloredBlur');
|
|
1181
|
+
feMerge.append('feMergeNode').attr('in', 'SourceGraphic');
|
|
1182
|
+
const axisGrid = g.append('g').attr('class', 'axis-wrapper');
|
|
1183
|
+
drawCircularGrid(axisGrid, radius, maxValue, config);
|
|
1184
|
+
drawAxis(axisGrid, axisNames, radiusScale, maxValue, angleSlice, config);
|
|
1185
|
+
createLegend(g, config);
|
|
1186
|
+
drawRadarChartBlobs(radiusScale, angleSlice, g, data, config);
|
|
1187
|
+
appendInvisibleTooltipCircles(g, data, radiusScale, angleSlice, config);
|
|
1188
|
+
return config;
|
|
1189
|
+
};
|
|
1190
|
+
|
|
1191
|
+
const d3ChartFactory = () => ({
|
|
1192
|
+
drawPieChart,
|
|
1193
|
+
drawRadarChart,
|
|
1194
|
+
drawBarChart,
|
|
1195
|
+
drawLineChart,
|
|
1196
|
+
drawForceDirectedChart,
|
|
1197
|
+
});
|
|
1198
|
+
const D3_CHART_FACTORY = new InjectionToken('D3_CHART_FACTORY', {
|
|
1199
|
+
providedIn: 'root',
|
|
1200
|
+
factory: d3ChartFactory,
|
|
1201
|
+
});
|
|
1202
|
+
|
|
1203
|
+
class AppBarChartComponent {
|
|
1204
|
+
constructor(doc, d3Factory) {
|
|
1205
|
+
this.doc = doc;
|
|
1206
|
+
this.d3Factory = d3Factory;
|
|
1207
|
+
/**
|
|
1208
|
+
* The chart id.
|
|
1209
|
+
*/
|
|
1210
|
+
this.chartId = 'bar-0';
|
|
1211
|
+
/**
|
|
1212
|
+
* The chart data.
|
|
1213
|
+
*/
|
|
1214
|
+
this.data = [];
|
|
1215
|
+
/**
|
|
1216
|
+
* The chart options.
|
|
1217
|
+
*/
|
|
1218
|
+
this.options = {};
|
|
1219
|
+
}
|
|
1220
|
+
/**
|
|
1221
|
+
* The chart options constructor.
|
|
1222
|
+
* @returns chart options
|
|
1223
|
+
*/
|
|
1224
|
+
chartOptions() {
|
|
1225
|
+
const bodyWidthAdjustment = 10;
|
|
1226
|
+
const width = Math.min(this.options.width ?? defaultBarChartConfig.width, this.doc.body.clientWidth - defaultBarChartConfig.margin.left - defaultBarChartConfig.margin.right - bodyWidthAdjustment);
|
|
1227
|
+
const height = Math.min(this.options.height ?? width, this.doc.body.clientWidth - defaultBarChartConfig.margin.top - defaultBarChartConfig.margin.bottom - bodyWidthAdjustment);
|
|
1228
|
+
const yAxisTicks = Math.max(...this.data.map(item => item.value));
|
|
1229
|
+
const options = {
|
|
1230
|
+
width,
|
|
1231
|
+
height,
|
|
1232
|
+
yAxisTicks,
|
|
1233
|
+
...this.options,
|
|
1234
|
+
};
|
|
1235
|
+
return options;
|
|
1236
|
+
}
|
|
1237
|
+
/**
|
|
1238
|
+
* Draws the chart.
|
|
1239
|
+
*/
|
|
1240
|
+
drawChart() {
|
|
1241
|
+
if (typeof this.container !== 'undefined') {
|
|
1242
|
+
const options = this.chartOptions();
|
|
1243
|
+
this.d3Factory.drawBarChart(this.container, this.data, options);
|
|
1244
|
+
}
|
|
1245
|
+
}
|
|
1246
|
+
/**
|
|
1247
|
+
* Actually draws the chart after the component view is initialized.
|
|
1248
|
+
*/
|
|
1249
|
+
ngAfterViewInit() {
|
|
1250
|
+
this.drawChart();
|
|
1251
|
+
}
|
|
1252
|
+
/**
|
|
1253
|
+
* Redraws the chart on changes.
|
|
1254
|
+
*/
|
|
1255
|
+
ngOnChanges(changes) {
|
|
1256
|
+
const data = changes.data?.currentValue;
|
|
1257
|
+
const options = changes.options?.currentValue;
|
|
1258
|
+
if ((typeof data !== 'undefined' && data !== null) || (typeof options !== 'undefined' && options !== null)) {
|
|
1259
|
+
this.drawChart();
|
|
1260
|
+
}
|
|
1261
|
+
}
|
|
1262
|
+
}
|
|
1263
|
+
/** @nocollapse */ AppBarChartComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.0.6", ngImport: i0, type: AppBarChartComponent, deps: [{ token: DOCUMENT }, { token: D3_CHART_FACTORY }], target: i0.ɵɵFactoryTarget.Component });
|
|
1264
|
+
/** @nocollapse */ AppBarChartComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "14.0.6", type: AppBarChartComponent, selector: "app-bar-chart", inputs: { chartId: "chartId", data: "data", options: "options" }, viewQueries: [{ propertyName: "container", first: true, predicate: ["container"], descendants: true }], usesOnChanges: true, ngImport: i0, template: "<div class=\"container\" id=\"{{ chartId }}\" #container></div>\n", styles: [":host{display:flex;flex-direction:row;flex-wrap:nowrap;justify-content:center}:host .container{flex:0 1 auto}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
1265
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.0.6", ngImport: i0, type: AppBarChartComponent, decorators: [{
|
|
1266
|
+
type: Component,
|
|
1267
|
+
args: [{ selector: 'app-bar-chart', changeDetection: ChangeDetectionStrategy.OnPush, template: "<div class=\"container\" id=\"{{ chartId }}\" #container></div>\n", styles: [":host{display:flex;flex-direction:row;flex-wrap:nowrap;justify-content:center}:host .container{flex:0 1 auto}\n"] }]
|
|
1268
|
+
}], ctorParameters: function () { return [{ type: Document, decorators: [{
|
|
1269
|
+
type: Inject,
|
|
1270
|
+
args: [DOCUMENT]
|
|
1271
|
+
}] }, { type: undefined, decorators: [{
|
|
1272
|
+
type: Inject,
|
|
1273
|
+
args: [D3_CHART_FACTORY]
|
|
1274
|
+
}] }]; }, propDecorators: { chartId: [{
|
|
1275
|
+
type: Input
|
|
1276
|
+
}], data: [{
|
|
1277
|
+
type: Input
|
|
1278
|
+
}], options: [{
|
|
1279
|
+
type: Input
|
|
1280
|
+
}], container: [{
|
|
1281
|
+
type: ViewChild,
|
|
1282
|
+
args: ['container']
|
|
1283
|
+
}] } });
|
|
1284
|
+
|
|
1285
|
+
class AppPieChartComponent {
|
|
1286
|
+
constructor(doc, d3Factory) {
|
|
1287
|
+
this.doc = doc;
|
|
1288
|
+
this.d3Factory = d3Factory;
|
|
1289
|
+
/**
|
|
1290
|
+
* The chart id.
|
|
1291
|
+
*/
|
|
1292
|
+
this.chartId = 'pie-0';
|
|
1293
|
+
/**
|
|
1294
|
+
* The chart data.
|
|
1295
|
+
*/
|
|
1296
|
+
this.data = [];
|
|
1297
|
+
/**
|
|
1298
|
+
* The chart options.
|
|
1299
|
+
*/
|
|
1300
|
+
this.options = {};
|
|
1301
|
+
}
|
|
1302
|
+
/**
|
|
1303
|
+
* The chart options constructor.
|
|
1304
|
+
* @returns chart options
|
|
1305
|
+
*/
|
|
1306
|
+
chartOptions() {
|
|
1307
|
+
const margin = { top: 50, right: 50, bottom: 50, left: 50 };
|
|
1308
|
+
const minWidth = 350;
|
|
1309
|
+
const modifiers = {
|
|
1310
|
+
width: 10,
|
|
1311
|
+
height: 20,
|
|
1312
|
+
};
|
|
1313
|
+
const width = Math.min(minWidth, this.doc.body.clientWidth - modifiers.width) - margin.left - margin.right;
|
|
1314
|
+
const height = Math.min(width, this.doc.body.clientHeight - margin.top - margin.bottom - modifiers.height);
|
|
1315
|
+
const options = {
|
|
1316
|
+
width,
|
|
1317
|
+
height,
|
|
1318
|
+
margin,
|
|
1319
|
+
...this.options,
|
|
1320
|
+
};
|
|
1321
|
+
return options;
|
|
1322
|
+
}
|
|
1323
|
+
/**
|
|
1324
|
+
* Draws the chart.
|
|
1325
|
+
*/
|
|
1326
|
+
drawChart() {
|
|
1327
|
+
if (typeof this.container !== 'undefined') {
|
|
1328
|
+
const options = this.chartOptions();
|
|
1329
|
+
this.d3Factory.drawPieChart(this.container, this.data, options);
|
|
1330
|
+
}
|
|
1331
|
+
}
|
|
1332
|
+
/**
|
|
1333
|
+
* Actually draws the chart after the component view is initialized.
|
|
1334
|
+
*/
|
|
1335
|
+
ngAfterViewInit() {
|
|
1336
|
+
this.drawChart();
|
|
1337
|
+
}
|
|
1338
|
+
/**
|
|
1339
|
+
* Redraws the chart on changes.
|
|
1340
|
+
*/
|
|
1341
|
+
ngOnChanges(changes) {
|
|
1342
|
+
const data = changes.data?.currentValue;
|
|
1343
|
+
const options = changes.options?.currentValue;
|
|
1344
|
+
if ((typeof data !== 'undefined' && data !== null) || (typeof options !== 'undefined' && options !== null)) {
|
|
1345
|
+
this.drawChart();
|
|
1346
|
+
}
|
|
1347
|
+
}
|
|
1348
|
+
}
|
|
1349
|
+
/** @nocollapse */ AppPieChartComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.0.6", ngImport: i0, type: AppPieChartComponent, deps: [{ token: DOCUMENT }, { token: D3_CHART_FACTORY }], target: i0.ɵɵFactoryTarget.Component });
|
|
1350
|
+
/** @nocollapse */ AppPieChartComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "14.0.6", type: AppPieChartComponent, selector: "app-pie-chart", inputs: { chartId: "chartId", data: "data", options: "options" }, viewQueries: [{ propertyName: "container", first: true, predicate: ["container"], descendants: true }], usesOnChanges: true, ngImport: i0, template: "<div class=\"container\">\n <div id=\"{{ chartId }}\" #container></div>\n\n <small class=\"container--chart-title\">{{ options.chartTitle }}</small>\n</div>\n", styles: [":host{display:flex;flex-direction:row;flex-wrap:nowrap;justify-content:center}:host canvas{width:auto!important;height:150px}:host .container{flex:0 1 auto}:host .container--chart-title{display:block;text-align:center}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
1351
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.0.6", ngImport: i0, type: AppPieChartComponent, decorators: [{
|
|
1352
|
+
type: Component,
|
|
1353
|
+
args: [{ selector: 'app-pie-chart', changeDetection: ChangeDetectionStrategy.OnPush, template: "<div class=\"container\">\n <div id=\"{{ chartId }}\" #container></div>\n\n <small class=\"container--chart-title\">{{ options.chartTitle }}</small>\n</div>\n", styles: [":host{display:flex;flex-direction:row;flex-wrap:nowrap;justify-content:center}:host canvas{width:auto!important;height:150px}:host .container{flex:0 1 auto}:host .container--chart-title{display:block;text-align:center}\n"] }]
|
|
1354
|
+
}], ctorParameters: function () { return [{ type: Document, decorators: [{
|
|
1355
|
+
type: Inject,
|
|
1356
|
+
args: [DOCUMENT]
|
|
1357
|
+
}] }, { type: undefined, decorators: [{
|
|
1358
|
+
type: Inject,
|
|
1359
|
+
args: [D3_CHART_FACTORY]
|
|
1360
|
+
}] }]; }, propDecorators: { chartId: [{
|
|
1361
|
+
type: Input
|
|
1362
|
+
}], data: [{
|
|
1363
|
+
type: Input
|
|
1364
|
+
}], options: [{
|
|
1365
|
+
type: Input
|
|
1366
|
+
}], container: [{
|
|
1367
|
+
type: ViewChild,
|
|
1368
|
+
args: ['container']
|
|
1369
|
+
}] } });
|
|
1370
|
+
|
|
1371
|
+
class AppRadarChartComponent {
|
|
1372
|
+
constructor(doc, d3Factory) {
|
|
1373
|
+
this.doc = doc;
|
|
1374
|
+
this.d3Factory = d3Factory;
|
|
1375
|
+
/**
|
|
1376
|
+
* The chart id.
|
|
1377
|
+
*/
|
|
1378
|
+
this.chartId = 'radar-0';
|
|
1379
|
+
/**
|
|
1380
|
+
* The chart data.
|
|
1381
|
+
*/
|
|
1382
|
+
this.data = [[]];
|
|
1383
|
+
/**
|
|
1384
|
+
* The chart options.
|
|
1385
|
+
*/
|
|
1386
|
+
this.options = {};
|
|
1387
|
+
}
|
|
1388
|
+
/**
|
|
1389
|
+
* The chart options constructor.
|
|
1390
|
+
* @returns chart options
|
|
1391
|
+
*/
|
|
1392
|
+
chartOptions() {
|
|
1393
|
+
const xsOffset = 500;
|
|
1394
|
+
const smOffset = 800;
|
|
1395
|
+
const mdOffset = 1024;
|
|
1396
|
+
const labelFactorDefault = 1.15;
|
|
1397
|
+
const labelFactorMd = 1.15;
|
|
1398
|
+
const labelFactorSm = 1.15;
|
|
1399
|
+
const labelFactorXs = 1.4;
|
|
1400
|
+
const wrapWidthDefault = 85;
|
|
1401
|
+
const wrapWidthMd = 80;
|
|
1402
|
+
const wrapWidthXs = 70;
|
|
1403
|
+
const bodyWidthAdjustment = 10;
|
|
1404
|
+
const width = Math.min(this.options.width ?? defaultRadarChartConfig.width, this.doc.body.clientWidth - defaultRadarChartConfig.margin.left - defaultRadarChartConfig.margin.right - bodyWidthAdjustment);
|
|
1405
|
+
const height = Math.min(width, this.doc.body.clientHeight - defaultRadarChartConfig.margin.top - defaultRadarChartConfig.margin.bottom - bodyWidthAdjustment);
|
|
1406
|
+
const labelFactor = width <= xsOffset ? labelFactorXs : width <= smOffset ? labelFactorSm : width <= mdOffset ? labelFactorMd : labelFactorDefault;
|
|
1407
|
+
const labelTextWrapWidth = width <= xsOffset ? wrapWidthXs : width <= mdOffset ? wrapWidthMd : wrapWidthDefault;
|
|
1408
|
+
const options = {
|
|
1409
|
+
width,
|
|
1410
|
+
height,
|
|
1411
|
+
maxValue: this.data[0].reduce((accumulator, item) => (item.value > accumulator ? item.value : accumulator), 0) + 1,
|
|
1412
|
+
levels: 5,
|
|
1413
|
+
roundStrokes: true,
|
|
1414
|
+
labelFactor,
|
|
1415
|
+
labelTextWrapWidth,
|
|
1416
|
+
...this.options,
|
|
1417
|
+
};
|
|
1418
|
+
return options;
|
|
1419
|
+
}
|
|
1420
|
+
/**
|
|
1421
|
+
* Draws the chart.
|
|
1422
|
+
*/
|
|
1423
|
+
drawChart() {
|
|
1424
|
+
if (typeof this.container !== 'undefined') {
|
|
1425
|
+
const options = this.chartOptions();
|
|
1426
|
+
this.d3Factory.drawRadarChart(this.container, this.data, options);
|
|
1427
|
+
}
|
|
1428
|
+
}
|
|
1429
|
+
/**
|
|
1430
|
+
* Actually draws the chart after the component view is initialized.
|
|
1431
|
+
*/
|
|
1432
|
+
ngAfterViewInit() {
|
|
1433
|
+
this.drawChart();
|
|
1434
|
+
}
|
|
1435
|
+
/**
|
|
1436
|
+
* Redraws the chart on changes.
|
|
1437
|
+
*/
|
|
1438
|
+
ngOnChanges(changes) {
|
|
1439
|
+
const currentValue = changes.data?.currentValue;
|
|
1440
|
+
const options = changes.options?.currentValue;
|
|
1441
|
+
if ((typeof currentValue !== 'undefined' && currentValue !== null) || (typeof options !== 'undefined' && options !== null)) {
|
|
1442
|
+
this.drawChart();
|
|
1443
|
+
}
|
|
1444
|
+
}
|
|
1445
|
+
}
|
|
1446
|
+
/** @nocollapse */ AppRadarChartComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.0.6", ngImport: i0, type: AppRadarChartComponent, deps: [{ token: DOCUMENT }, { token: D3_CHART_FACTORY }], target: i0.ɵɵFactoryTarget.Component });
|
|
1447
|
+
/** @nocollapse */ AppRadarChartComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "14.0.6", type: AppRadarChartComponent, selector: "app-radar-chart", inputs: { chartId: "chartId", data: "data", options: "options" }, viewQueries: [{ propertyName: "container", first: true, predicate: ["container"], descendants: true }], usesOnChanges: true, ngImport: i0, template: "<div class=\"container\" id=\"{{ chartId }}\" #container></div>\n", styles: [":host{display:flex;flex-direction:row;flex-wrap:nowrap;justify-content:center}:host .container{flex:0 1 auto}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
1448
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.0.6", ngImport: i0, type: AppRadarChartComponent, decorators: [{
|
|
1449
|
+
type: Component,
|
|
1450
|
+
args: [{ selector: 'app-radar-chart', changeDetection: ChangeDetectionStrategy.OnPush, template: "<div class=\"container\" id=\"{{ chartId }}\" #container></div>\n", styles: [":host{display:flex;flex-direction:row;flex-wrap:nowrap;justify-content:center}:host .container{flex:0 1 auto}\n"] }]
|
|
1451
|
+
}], ctorParameters: function () { return [{ type: Document, decorators: [{
|
|
1452
|
+
type: Inject,
|
|
1453
|
+
args: [DOCUMENT]
|
|
1454
|
+
}] }, { type: undefined, decorators: [{
|
|
1455
|
+
type: Inject,
|
|
1456
|
+
args: [D3_CHART_FACTORY]
|
|
1457
|
+
}] }]; }, propDecorators: { chartId: [{
|
|
1458
|
+
type: Input
|
|
1459
|
+
}], data: [{
|
|
1460
|
+
type: Input
|
|
1461
|
+
}], options: [{
|
|
1462
|
+
type: Input
|
|
1463
|
+
}], container: [{
|
|
1464
|
+
type: ViewChild,
|
|
1465
|
+
args: ['container']
|
|
1466
|
+
}] } });
|
|
1467
|
+
|
|
1468
|
+
class AppForceDirectedChartComponent {
|
|
1469
|
+
constructor(doc, d3Factory) {
|
|
1470
|
+
this.doc = doc;
|
|
1471
|
+
this.d3Factory = d3Factory;
|
|
1472
|
+
/**
|
|
1473
|
+
* The chart identifier.
|
|
1474
|
+
*/
|
|
1475
|
+
this.chartId = 'force-0';
|
|
1476
|
+
/**
|
|
1477
|
+
* The chart data.
|
|
1478
|
+
*/
|
|
1479
|
+
this.data = {
|
|
1480
|
+
domains: [],
|
|
1481
|
+
entities: [],
|
|
1482
|
+
links: [],
|
|
1483
|
+
nodes: [],
|
|
1484
|
+
};
|
|
1485
|
+
/**
|
|
1486
|
+
* The chart options.
|
|
1487
|
+
*/
|
|
1488
|
+
this.options = {};
|
|
1489
|
+
}
|
|
1490
|
+
/**
|
|
1491
|
+
* The chart options constructor.
|
|
1492
|
+
* @returns chart options
|
|
1493
|
+
*/
|
|
1494
|
+
chartOptions() {
|
|
1495
|
+
const margin = { top: 50, right: 50, bottom: 50, left: 50 };
|
|
1496
|
+
const minWidth = 350;
|
|
1497
|
+
const modifiers = {
|
|
1498
|
+
width: 10,
|
|
1499
|
+
height: 20,
|
|
1500
|
+
};
|
|
1501
|
+
const width = Math.min(minWidth, this.doc.body.clientWidth - modifiers.width) - margin.left - margin.right;
|
|
1502
|
+
const height = Math.min(width, this.doc.body.clientHeight - margin.top - margin.bottom - modifiers.height);
|
|
1503
|
+
const options = { width, height, margin, ...this.options };
|
|
1504
|
+
return options;
|
|
1505
|
+
}
|
|
1506
|
+
/**
|
|
1507
|
+
* Draws the chart.
|
|
1508
|
+
*/
|
|
1509
|
+
drawChart() {
|
|
1510
|
+
if (typeof this.container !== 'undefined') {
|
|
1511
|
+
const options = this.chartOptions();
|
|
1512
|
+
this.d3Factory.drawForceDirectedChart(this.container, this.data, options);
|
|
1513
|
+
}
|
|
1514
|
+
}
|
|
1515
|
+
/**
|
|
1516
|
+
* Actually draws the chart after the component view is initialized.
|
|
1517
|
+
*/
|
|
1518
|
+
ngAfterViewInit() {
|
|
1519
|
+
this.drawChart();
|
|
1520
|
+
}
|
|
1521
|
+
/**
|
|
1522
|
+
* Redraws the chart on changes.
|
|
1523
|
+
*/
|
|
1524
|
+
ngOnChanges(changes) {
|
|
1525
|
+
const prevData = changes.data?.previousValue;
|
|
1526
|
+
const nextData = changes.data?.currentValue;
|
|
1527
|
+
const options = changes.options?.currentValue;
|
|
1528
|
+
if ((Boolean(changes.data?.currentValue) && (prevData?.nodes ?? []).length !== (nextData?.nodes ?? []).length) ||
|
|
1529
|
+
(typeof options !== 'undefined' && options !== null)) {
|
|
1530
|
+
this.drawChart();
|
|
1531
|
+
}
|
|
1532
|
+
}
|
|
1533
|
+
}
|
|
1534
|
+
/** @nocollapse */ AppForceDirectedChartComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.0.6", ngImport: i0, type: AppForceDirectedChartComponent, deps: [{ token: DOCUMENT }, { token: D3_CHART_FACTORY }], target: i0.ɵɵFactoryTarget.Component });
|
|
1535
|
+
/** @nocollapse */ AppForceDirectedChartComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "14.0.6", type: AppForceDirectedChartComponent, selector: "app-force-directed-chart", inputs: { chartId: "chartId", data: "data", options: "options" }, viewQueries: [{ propertyName: "container", first: true, predicate: ["container"], descendants: true }], usesOnChanges: true, ngImport: i0, template: "<div class=\"container\">\n <div id=\"{{ chartId }}\" #container></div>\n\n <small class=\"container--chart-title\">{{ options.chartTitle }}</small>\n</div>\n", styles: [":host{display:flex;flex-direction:row;flex-wrap:nowrap;justify-content:center}:host .container{flex:0 1 auto}:host .container--chart-title{display:block;text-align:center}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
1536
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.0.6", ngImport: i0, type: AppForceDirectedChartComponent, decorators: [{
|
|
1537
|
+
type: Component,
|
|
1538
|
+
args: [{ selector: 'app-force-directed-chart', changeDetection: ChangeDetectionStrategy.OnPush, template: "<div class=\"container\">\n <div id=\"{{ chartId }}\" #container></div>\n\n <small class=\"container--chart-title\">{{ options.chartTitle }}</small>\n</div>\n", styles: [":host{display:flex;flex-direction:row;flex-wrap:nowrap;justify-content:center}:host .container{flex:0 1 auto}:host .container--chart-title{display:block;text-align:center}\n"] }]
|
|
1539
|
+
}], ctorParameters: function () { return [{ type: Document, decorators: [{
|
|
1540
|
+
type: Inject,
|
|
1541
|
+
args: [DOCUMENT]
|
|
1542
|
+
}] }, { type: undefined, decorators: [{
|
|
1543
|
+
type: Inject,
|
|
1544
|
+
args: [D3_CHART_FACTORY]
|
|
1545
|
+
}] }]; }, propDecorators: { chartId: [{
|
|
1546
|
+
type: Input
|
|
1547
|
+
}], data: [{
|
|
1548
|
+
type: Input
|
|
1549
|
+
}], options: [{
|
|
1550
|
+
type: Input
|
|
1551
|
+
}], container: [{
|
|
1552
|
+
type: ViewChild,
|
|
1553
|
+
args: ['container']
|
|
1554
|
+
}] } });
|
|
1555
|
+
|
|
1556
|
+
class AppLineChartComponent {
|
|
1557
|
+
constructor(doc, d3Factory) {
|
|
1558
|
+
this.doc = doc;
|
|
1559
|
+
this.d3Factory = d3Factory;
|
|
1560
|
+
/**
|
|
1561
|
+
* The chart id.
|
|
1562
|
+
*/
|
|
1563
|
+
this.chartId = 'line-0';
|
|
1564
|
+
/**
|
|
1565
|
+
* The chart data.
|
|
1566
|
+
*/
|
|
1567
|
+
this.data = [];
|
|
1568
|
+
/**
|
|
1569
|
+
* The chart options.
|
|
1570
|
+
*/
|
|
1571
|
+
this.options = {};
|
|
1572
|
+
}
|
|
1573
|
+
/**
|
|
1574
|
+
* The chart options constructor.
|
|
1575
|
+
* @returns chart options
|
|
1576
|
+
*/
|
|
1577
|
+
chartOptions() {
|
|
1578
|
+
const bodyWidthAdjustment = 10;
|
|
1579
|
+
const width = Math.min(this.options.width ?? defaultLineChartConfig.width, this.doc.body.clientWidth - defaultLineChartConfig.margin.left - defaultLineChartConfig.margin.right - bodyWidthAdjustment);
|
|
1580
|
+
const xTicksScale = 50;
|
|
1581
|
+
const ticks = {
|
|
1582
|
+
x: width / xTicksScale,
|
|
1583
|
+
y: Math.max(...this.data.map(item => item.value)),
|
|
1584
|
+
};
|
|
1585
|
+
const height = Math.min(this.options.height ?? width, this.doc.body.clientWidth - defaultLineChartConfig.margin.top - defaultLineChartConfig.margin.bottom - bodyWidthAdjustment);
|
|
1586
|
+
const options = {
|
|
1587
|
+
width,
|
|
1588
|
+
height,
|
|
1589
|
+
ticks,
|
|
1590
|
+
...this.options,
|
|
1591
|
+
};
|
|
1592
|
+
return options;
|
|
1593
|
+
}
|
|
1594
|
+
/**
|
|
1595
|
+
* Draws the chart.
|
|
1596
|
+
*/
|
|
1597
|
+
drawChart() {
|
|
1598
|
+
if (typeof this.container !== 'undefined') {
|
|
1599
|
+
const options = this.chartOptions();
|
|
1600
|
+
this.d3Factory.drawLineChart(this.container, this.data, options);
|
|
1601
|
+
}
|
|
1602
|
+
}
|
|
1603
|
+
/**
|
|
1604
|
+
* Actually draws the chart after the component view is initialized.
|
|
1605
|
+
*/
|
|
1606
|
+
ngAfterViewInit() {
|
|
1607
|
+
this.drawChart();
|
|
1608
|
+
}
|
|
1609
|
+
/**
|
|
1610
|
+
* Redraws the chart on changes.
|
|
1611
|
+
*/
|
|
1612
|
+
ngOnChanges(changes) {
|
|
1613
|
+
const data = changes.data?.currentValue;
|
|
1614
|
+
const options = changes.options?.currentValue;
|
|
1615
|
+
if ((typeof data !== 'undefined' && data !== null) || (typeof options !== 'undefined' && options !== null)) {
|
|
1616
|
+
this.drawChart();
|
|
1617
|
+
}
|
|
1618
|
+
}
|
|
1619
|
+
}
|
|
1620
|
+
/** @nocollapse */ AppLineChartComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.0.6", ngImport: i0, type: AppLineChartComponent, deps: [{ token: DOCUMENT }, { token: D3_CHART_FACTORY }], target: i0.ɵɵFactoryTarget.Component });
|
|
1621
|
+
/** @nocollapse */ AppLineChartComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "14.0.6", type: AppLineChartComponent, selector: "app-line-chart", inputs: { chartId: "chartId", data: "data", options: "options" }, viewQueries: [{ propertyName: "container", first: true, predicate: ["container"], descendants: true }], usesOnChanges: true, ngImport: i0, template: "<div class=\"container\" id=\"{{ chartId }}\" #container></div>\n", styles: [":host{display:flex;flex-direction:row;flex-wrap:nowrap;justify-content:center}:host .container{flex:0 1 auto}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
1622
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.0.6", ngImport: i0, type: AppLineChartComponent, decorators: [{
|
|
1623
|
+
type: Component,
|
|
1624
|
+
args: [{ selector: 'app-line-chart', changeDetection: ChangeDetectionStrategy.OnPush, template: "<div class=\"container\" id=\"{{ chartId }}\" #container></div>\n", styles: [":host{display:flex;flex-direction:row;flex-wrap:nowrap;justify-content:center}:host .container{flex:0 1 auto}\n"] }]
|
|
1625
|
+
}], ctorParameters: function () { return [{ type: Document, decorators: [{
|
|
1626
|
+
type: Inject,
|
|
1627
|
+
args: [DOCUMENT]
|
|
1628
|
+
}] }, { type: undefined, decorators: [{
|
|
1629
|
+
type: Inject,
|
|
1630
|
+
args: [D3_CHART_FACTORY]
|
|
1631
|
+
}] }]; }, propDecorators: { chartId: [{
|
|
1632
|
+
type: Input
|
|
1633
|
+
}], data: [{
|
|
1634
|
+
type: Input
|
|
1635
|
+
}], options: [{
|
|
1636
|
+
type: Input
|
|
1637
|
+
}], container: [{
|
|
1638
|
+
type: ViewChild,
|
|
1639
|
+
args: ['container']
|
|
1640
|
+
}] } });
|
|
1641
|
+
|
|
1642
|
+
class AppChartExamplesComponent {
|
|
1643
|
+
constructor(breakpointObserver) {
|
|
1644
|
+
this.breakpointObserver = breakpointObserver;
|
|
1645
|
+
this.breakpoint$ = this.breakpointObserver
|
|
1646
|
+
.observe([Breakpoints.XSmall, Breakpoints.Small, Breakpoints.Medium, Breakpoints.Large, Breakpoints.XLarge])
|
|
1647
|
+
.pipe(map(result => Object.keys(result.breakpoints).find(item => result.breakpoints[item]) ?? 'unknown'));
|
|
1648
|
+
this.barChartConfig$ = this.breakpoint$.pipe(switchMap(() => timer(this.timeout).pipe(first(), map(() => ({ data: this.barChartData, options: this.barChartOptions() })))));
|
|
1649
|
+
this.lineChartConfig$ = this.breakpoint$.pipe(switchMap(() => timer(this.timeout).pipe(first(), map(() => ({ data: this.lineChartData, options: this.lineChartOptions() })))));
|
|
1650
|
+
this.radarChartConfig$ = this.breakpoint$.pipe(switchMap(() => timer(this.timeout).pipe(first(), map(() => ({ data: this.radarChartData, options: this.radarChartOptions() })))));
|
|
1651
|
+
this.pieChartConfig$ = this.breakpoint$.pipe(switchMap(() => timer(this.timeout).pipe(first(), map(() => ({ data: this.pieChartData, options: this.pieChartOptions() })))));
|
|
1652
|
+
this.forceDirectedChartConfig$ = this.breakpoint$.pipe(switchMap(() => timer(this.timeout).pipe(first(), map(() => ({ data: this.forceDirectedChartData, options: this.forceDirectedChartOptions() })))));
|
|
1653
|
+
this.timeout = 100;
|
|
1654
|
+
}
|
|
1655
|
+
/**
|
|
1656
|
+
* Sample bar chart data.
|
|
1657
|
+
*/
|
|
1658
|
+
get barChartData() {
|
|
1659
|
+
return [
|
|
1660
|
+
{ title: 'one', value: 1 },
|
|
1661
|
+
{ title: 'two', value: 2 },
|
|
1662
|
+
{ title: 'three', value: 3 },
|
|
1663
|
+
{ title: 'four', value: 4 },
|
|
1664
|
+
{ title: 'five', value: 5 },
|
|
1665
|
+
];
|
|
1666
|
+
}
|
|
1667
|
+
/**
|
|
1668
|
+
* Sample line chart data.
|
|
1669
|
+
*/
|
|
1670
|
+
get lineChartData() {
|
|
1671
|
+
const increment = 10000;
|
|
1672
|
+
const multiplier = 2;
|
|
1673
|
+
return [
|
|
1674
|
+
{ timestamp: new Date().getTime(), value: 1 },
|
|
1675
|
+
{ timestamp: new Date().getTime() + increment, value: 10 },
|
|
1676
|
+
{ timestamp: new Date().getTime() + increment * multiplier, value: 3 },
|
|
1677
|
+
{ timestamp: new Date().getTime() + increment * Math.pow(multiplier, multiplier), value: 5 },
|
|
1678
|
+
{ timestamp: new Date().getTime() + increment * Math.pow(multiplier, multiplier) * multiplier, value: 4 },
|
|
1679
|
+
{ timestamp: new Date().getTime() + increment * Math.pow(multiplier, multiplier) * Math.pow(multiplier, multiplier), value: 7 },
|
|
1680
|
+
{
|
|
1681
|
+
timestamp: new Date().getTime() + increment * Math.pow(multiplier, multiplier) * Math.pow(multiplier, multiplier) * multiplier,
|
|
1682
|
+
value: 8,
|
|
1683
|
+
},
|
|
1684
|
+
];
|
|
1685
|
+
}
|
|
1686
|
+
/**
|
|
1687
|
+
* Sample radar chart data.
|
|
1688
|
+
*/
|
|
1689
|
+
get radarChartData() {
|
|
1690
|
+
return [
|
|
1691
|
+
[
|
|
1692
|
+
{ axis: 'one', value: 1, unit: 'x' },
|
|
1693
|
+
{ axis: 'two', value: 2, unit: 'x' },
|
|
1694
|
+
{ axis: 'three', value: 3, unit: 'x' },
|
|
1695
|
+
{ axis: 'four', value: 4, unit: 'x' },
|
|
1696
|
+
{ axis: 'five', value: 5, unit: 'x' },
|
|
1697
|
+
{ axis: 'six', value: 6, unit: 'x' },
|
|
1698
|
+
{ axis: 'seven', value: 7, unit: 'x' },
|
|
1699
|
+
{ axis: 'eight', value: 8, unit: 'x' },
|
|
1700
|
+
{ axis: 'nine (long labels are wrapped)', value: 9, unit: 'x' },
|
|
1701
|
+
],
|
|
1702
|
+
[
|
|
1703
|
+
{ axis: 'one', value: 9, unit: 'y' },
|
|
1704
|
+
{ axis: 'two', value: 8, unit: 'y' },
|
|
1705
|
+
{ axis: 'three', value: 7, unit: 'y' },
|
|
1706
|
+
{ axis: 'four', value: 6, unit: 'y' },
|
|
1707
|
+
{ axis: 'five', value: 5, unit: 'y' },
|
|
1708
|
+
{ axis: 'six', value: 4, unit: 'y' },
|
|
1709
|
+
{ axis: 'seven', value: 3, unit: 'y' },
|
|
1710
|
+
{ axis: 'eight', value: 2, unit: 'y' },
|
|
1711
|
+
{ axis: 'nine (long labels are wrapped)', value: 1, unit: 'y' },
|
|
1712
|
+
],
|
|
1713
|
+
];
|
|
1714
|
+
}
|
|
1715
|
+
/**
|
|
1716
|
+
* Sample pie chart data.
|
|
1717
|
+
*/
|
|
1718
|
+
get pieChartData() {
|
|
1719
|
+
return [
|
|
1720
|
+
{ key: 'one', y: 1 },
|
|
1721
|
+
{ key: 'two', y: 2 },
|
|
1722
|
+
{ key: 'three', y: 3 },
|
|
1723
|
+
{ key: 'four', y: 4 },
|
|
1724
|
+
{ key: 'five', y: 5 },
|
|
1725
|
+
{ key: 'six', y: 6 },
|
|
1726
|
+
];
|
|
1727
|
+
}
|
|
1728
|
+
/**
|
|
1729
|
+
* Sample force directed chart data.
|
|
1730
|
+
*/
|
|
1731
|
+
get forceDirectedChartData() {
|
|
1732
|
+
const input = {
|
|
1733
|
+
domains: ['first', 'second', 'third'],
|
|
1734
|
+
entities: [
|
|
1735
|
+
{ name: 'one', domains: ['first'], img: '' },
|
|
1736
|
+
{ name: 'two', domains: ['second'], img: '' },
|
|
1737
|
+
{ name: 'three', domains: ['third'], img: '' },
|
|
1738
|
+
{ name: 'four', domains: ['first', 'second'], img: '' },
|
|
1739
|
+
{ name: 'five', domains: ['second'], img: '' },
|
|
1740
|
+
{ name: 'six', domains: ['third', 'second'], img: '' },
|
|
1741
|
+
{ name: 'seven', domains: ['second'], img: '' },
|
|
1742
|
+
{ name: 'eight', domains: ['third'], img: '' },
|
|
1743
|
+
],
|
|
1744
|
+
};
|
|
1745
|
+
const domains = input.domains.map((name, index) => ({ index, name, value: 1 }));
|
|
1746
|
+
const entities = input.entities.map((app, index) => ({
|
|
1747
|
+
index: index,
|
|
1748
|
+
name: app.name,
|
|
1749
|
+
domains: [...app.domains],
|
|
1750
|
+
img: app.img,
|
|
1751
|
+
linksCount: 0,
|
|
1752
|
+
}));
|
|
1753
|
+
const nodes = [...entities];
|
|
1754
|
+
const links = entities
|
|
1755
|
+
.map(entity => {
|
|
1756
|
+
return entity.domains.map(domain => {
|
|
1757
|
+
const source = domains.find(value => domain === value.name)?.index ?? -1;
|
|
1758
|
+
const target = entity.index;
|
|
1759
|
+
return { source, target };
|
|
1760
|
+
});
|
|
1761
|
+
})
|
|
1762
|
+
.reduce((accumulator, item) => (Array.isArray(item) ? [...accumulator, ...item] : [...accumulator, item]), [])
|
|
1763
|
+
.filter(link => link.source !== -1 && link.target !== -1 && typeof link.target !== 'undefined');
|
|
1764
|
+
const chartData = {
|
|
1765
|
+
domains,
|
|
1766
|
+
entities: entities.map(item => ({
|
|
1767
|
+
...item,
|
|
1768
|
+
linksCount: links.reduce((acc, link) => (link.target === item.index ? acc + 1 : acc), 0),
|
|
1769
|
+
})),
|
|
1770
|
+
links,
|
|
1771
|
+
nodes,
|
|
1772
|
+
};
|
|
1773
|
+
return chartData;
|
|
1774
|
+
}
|
|
1775
|
+
barChartOptions() {
|
|
1776
|
+
return {
|
|
1777
|
+
chartTitle: 'Example bar chart',
|
|
1778
|
+
xAxisTitle: 'long x axis title',
|
|
1779
|
+
yAxisTitle: 'long y axis title',
|
|
1780
|
+
};
|
|
1781
|
+
}
|
|
1782
|
+
lineChartOptions() {
|
|
1783
|
+
return {
|
|
1784
|
+
chartTitle: 'Example line chart',
|
|
1785
|
+
xAxisTitle: 'Date range',
|
|
1786
|
+
yAxisTitle: 'Value range',
|
|
1787
|
+
};
|
|
1788
|
+
}
|
|
1789
|
+
radarChartOptions() {
|
|
1790
|
+
return {
|
|
1791
|
+
chartTitle: 'Example radar chart',
|
|
1792
|
+
};
|
|
1793
|
+
}
|
|
1794
|
+
pieChartOptions() {
|
|
1795
|
+
return {
|
|
1796
|
+
chartTitle: 'Example pie chart',
|
|
1797
|
+
};
|
|
1798
|
+
}
|
|
1799
|
+
forceDirectedChartOptions() {
|
|
1800
|
+
return {
|
|
1801
|
+
chartTitle: 'Example force directed chart',
|
|
1802
|
+
};
|
|
1803
|
+
}
|
|
1804
|
+
}
|
|
1805
|
+
/** @nocollapse */ AppChartExamplesComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.0.6", ngImport: i0, type: AppChartExamplesComponent, deps: [{ token: i1.BreakpointObserver }], target: i0.ɵɵFactoryTarget.Component });
|
|
1806
|
+
/** @nocollapse */ AppChartExamplesComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "14.0.6", type: AppChartExamplesComponent, selector: "app-chart-examples", ngImport: i0, template: "<div class=\"container\" *ngIf=\"barChartConfig$ | async as config\">\n <app-bar-chart [chartId]=\"'bar-example-1'\" [data]=\"config.data\" [options]=\"config.options\"></app-bar-chart>\n</div>\n\n<hr [ngStyle]=\"{ width: '100%' }\" />\n\n<div class=\"container\" *ngIf=\"lineChartConfig$ | async as config\">\n <app-line-chart [chartId]=\"'line-example-1'\" [data]=\"config.data\" [options]=\"config.options\"></app-line-chart>\n</div>\n\n<hr [ngStyle]=\"{ width: '100%' }\" />\n\n<div class=\"container\" *ngIf=\"radarChartConfig$ | async as config\">\n <app-radar-chart [chartId]=\"'radar-example-1'\" [data]=\"config.data\" [options]=\"config.options\"></app-radar-chart>\n</div>\n\n<hr [ngStyle]=\"{ width: '100%' }\" />\n\n<div class=\"container\" *ngIf=\"pieChartConfig$ | async as config\">\n <app-pie-chart [chartId]=\"'pie-example-1'\" [data]=\"config.data\" [options]=\"config.options\"></app-pie-chart>\n</div>\n\n<hr [ngStyle]=\"{ width: '100%' }\" />\n\n<div class=\"container\" *ngIf=\"forceDirectedChartConfig$ | async as config\">\n <app-force-directed-chart\n [chartId]=\"'force-directed-example-1'\"\n [data]=\"config.data\"\n [options]=\"config.options\"\n ></app-force-directed-chart>\n</div>\n", styles: [":host{display:flex;flex-direction:row;flex-wrap:wrap;justify-content:center}:host .container{flex:1 1 auto;width:100%}\n"], dependencies: [{ kind: "directive", type: i2.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i2.NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }, { kind: "component", type: AppPieChartComponent, selector: "app-pie-chart", inputs: ["chartId", "data", "options"] }, { kind: "component", type: AppRadarChartComponent, selector: "app-radar-chart", inputs: ["chartId", "data", "options"] }, { kind: "component", type: AppForceDirectedChartComponent, selector: "app-force-directed-chart", inputs: ["chartId", "data", "options"] }, { kind: "component", type: AppBarChartComponent, selector: "app-bar-chart", inputs: ["chartId", "data", "options"] }, { kind: "component", type: AppLineChartComponent, selector: "app-line-chart", inputs: ["chartId", "data", "options"] }, { kind: "pipe", type: i2.AsyncPipe, name: "async" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
1807
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.0.6", ngImport: i0, type: AppChartExamplesComponent, decorators: [{
|
|
1808
|
+
type: Component,
|
|
1809
|
+
args: [{ selector: 'app-chart-examples', changeDetection: ChangeDetectionStrategy.OnPush, template: "<div class=\"container\" *ngIf=\"barChartConfig$ | async as config\">\n <app-bar-chart [chartId]=\"'bar-example-1'\" [data]=\"config.data\" [options]=\"config.options\"></app-bar-chart>\n</div>\n\n<hr [ngStyle]=\"{ width: '100%' }\" />\n\n<div class=\"container\" *ngIf=\"lineChartConfig$ | async as config\">\n <app-line-chart [chartId]=\"'line-example-1'\" [data]=\"config.data\" [options]=\"config.options\"></app-line-chart>\n</div>\n\n<hr [ngStyle]=\"{ width: '100%' }\" />\n\n<div class=\"container\" *ngIf=\"radarChartConfig$ | async as config\">\n <app-radar-chart [chartId]=\"'radar-example-1'\" [data]=\"config.data\" [options]=\"config.options\"></app-radar-chart>\n</div>\n\n<hr [ngStyle]=\"{ width: '100%' }\" />\n\n<div class=\"container\" *ngIf=\"pieChartConfig$ | async as config\">\n <app-pie-chart [chartId]=\"'pie-example-1'\" [data]=\"config.data\" [options]=\"config.options\"></app-pie-chart>\n</div>\n\n<hr [ngStyle]=\"{ width: '100%' }\" />\n\n<div class=\"container\" *ngIf=\"forceDirectedChartConfig$ | async as config\">\n <app-force-directed-chart\n [chartId]=\"'force-directed-example-1'\"\n [data]=\"config.data\"\n [options]=\"config.options\"\n ></app-force-directed-chart>\n</div>\n", styles: [":host{display:flex;flex-direction:row;flex-wrap:wrap;justify-content:center}:host .container{flex:1 1 auto;width:100%}\n"] }]
|
|
1810
|
+
}], ctorParameters: function () { return [{ type: i1.BreakpointObserver }]; } });
|
|
1811
|
+
|
|
1812
|
+
class AppClientD3ChartsModule {
|
|
1813
|
+
}
|
|
1814
|
+
/** @nocollapse */ AppClientD3ChartsModule.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.0.6", ngImport: i0, type: AppClientD3ChartsModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule });
|
|
1815
|
+
/** @nocollapse */ AppClientD3ChartsModule.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "14.0.6", ngImport: i0, type: AppClientD3ChartsModule, declarations: [AppPieChartComponent,
|
|
1816
|
+
AppRadarChartComponent,
|
|
1817
|
+
AppForceDirectedChartComponent,
|
|
1818
|
+
AppBarChartComponent,
|
|
1819
|
+
AppLineChartComponent,
|
|
1820
|
+
AppChartExamplesComponent], imports: [CommonModule], exports: [AppPieChartComponent,
|
|
1821
|
+
AppRadarChartComponent,
|
|
1822
|
+
AppForceDirectedChartComponent,
|
|
1823
|
+
AppBarChartComponent,
|
|
1824
|
+
AppLineChartComponent,
|
|
1825
|
+
AppChartExamplesComponent] });
|
|
1826
|
+
/** @nocollapse */ AppClientD3ChartsModule.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "14.0.6", ngImport: i0, type: AppClientD3ChartsModule, imports: [CommonModule] });
|
|
1827
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.0.6", ngImport: i0, type: AppClientD3ChartsModule, decorators: [{
|
|
1828
|
+
type: NgModule,
|
|
1829
|
+
args: [{
|
|
1830
|
+
imports: [CommonModule],
|
|
1831
|
+
declarations: [
|
|
1832
|
+
AppPieChartComponent,
|
|
1833
|
+
AppRadarChartComponent,
|
|
1834
|
+
AppForceDirectedChartComponent,
|
|
1835
|
+
AppBarChartComponent,
|
|
1836
|
+
AppLineChartComponent,
|
|
1837
|
+
AppChartExamplesComponent,
|
|
1838
|
+
],
|
|
1839
|
+
exports: [
|
|
1840
|
+
AppPieChartComponent,
|
|
1841
|
+
AppRadarChartComponent,
|
|
1842
|
+
AppForceDirectedChartComponent,
|
|
1843
|
+
AppBarChartComponent,
|
|
1844
|
+
AppLineChartComponent,
|
|
1845
|
+
AppChartExamplesComponent,
|
|
1846
|
+
],
|
|
1847
|
+
}]
|
|
1848
|
+
}] });
|
|
1849
|
+
|
|
1850
|
+
/**
|
|
1851
|
+
* Generated bundle index. Do not edit.
|
|
1852
|
+
*/
|
|
1853
|
+
|
|
1854
|
+
export { AppBarChartComponent, AppChartExamplesComponent, AppClientD3ChartsModule, AppForceDirectedChartComponent, AppLineChartComponent, AppPieChartComponent, AppRadarChartComponent, defaultBarChartConfig, defaultForceDirectedChartConfig, defaultLineChartConfig, defaultPieChartConfig, defaultRadarChartConfig, drawBarChart, drawForceDirectedChart, drawLineChart, drawPieChart, drawRadarChart };
|
|
1855
|
+
//# sourceMappingURL=rfprodz-client-d3-charts.mjs.map
|