@datarailsshared/dr_renderer 1.3.30 → 1.3.32

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@datarailsshared/dr_renderer",
3
- "version": "1.3.30",
3
+ "version": "1.3.32",
4
4
  "description": "DataRails charts and tables renderer",
5
5
  "keywords": [
6
6
  "datarails",
@@ -0,0 +1,323 @@
1
+ const _ = require('lodash');
2
+ const helpers = require("../dr-renderer-helpers");
3
+ const {DrGaugeChart} = require("./dr_gauge_chart");
4
+
5
+ const GAUGE_CATEGORIES_SUMMARY_OPTIONS_DEFAULT = {
6
+ gauge: {
7
+ background: "#fff",
8
+ startAngle: -90,
9
+ endAngle: 90,
10
+ thickness: 17,
11
+ tickLength: 0,
12
+ tickWidth: 0,
13
+ valueOffset: [0, 0, 0, 0],
14
+ offset: [12, 8, 12, 8],
15
+ goalIcon: '',
16
+ goalIconSize: [0, 0],
17
+ pivot: {
18
+ radius: 0,
19
+ color: "#808080",
20
+ },
21
+ colors: {
22
+ meta: "#333",
23
+ goal: "#4646CE",
24
+ },
25
+ },
26
+ goal: {
27
+ title: "Goal",
28
+ value: 1000000,
29
+ },
30
+ isAbsoluteValue: false,
31
+ segments: [
32
+ {
33
+ title: 'Completed',
34
+ count: 0,
35
+ isCompletionParameter: true,
36
+ color: '#037C5A',
37
+ },
38
+ {
39
+ title: 'Overdue',
40
+ count: 0,
41
+ color: '#BF1D30',
42
+ },
43
+ {
44
+ title: 'Pending',
45
+ count: 0,
46
+ color: '#FFA310',
47
+ },
48
+ {
49
+ title: 'Remaining',
50
+ count: 0,
51
+ color: '#F0F1F4',
52
+ }],
53
+ };
54
+
55
+ function DrGaugeCategoriesSummaryChart(pivotData, opts) {
56
+ this.completionPercentage = 0;
57
+
58
+ this.render = function () {
59
+ return DrGaugeChart.highchartsRenderer.ptCreateElementAndDraw(this.configChart(), opts);
60
+ };
61
+
62
+ this.createPlotBands = function (options) {
63
+ const {
64
+ segments,
65
+ goal: { value: goalValue },
66
+ gauge: { thickness },
67
+ } = options;
68
+
69
+ let bands = segments.map((item, index) => {
70
+ return {
71
+ from: item.from,
72
+ to: item.to,
73
+ color: item.color,
74
+ thickness: thickness,
75
+ title: item.title,
76
+ };
77
+ });
78
+
79
+ if (bands.length === 0) {
80
+ bands = [
81
+ {
82
+ from: 0,
83
+ to: 100,
84
+ color: '#F0F1F4',
85
+ thickness: thickness,
86
+ title: 'None',
87
+ }
88
+ ]
89
+ }
90
+
91
+ // clamp last segment
92
+ bands[bands.length - 1].to = Math.max(bands[bands.length - 1].to, goalValue);
93
+
94
+ return bands;
95
+ };
96
+
97
+ this.getDefaultValueForChart = function (type, existing_options) {
98
+ return DrGaugeChart.highchartsRenderer.getDefaultValueForChart(type, existing_options);
99
+ };
100
+
101
+ this.mergeOptions = function (options) {
102
+ return helpers.mergeDeep(
103
+ JSON.parse(JSON.stringify(GAUGE_CATEGORIES_SUMMARY_OPTIONS_DEFAULT)),
104
+ this.getDefaultValueForChart(DrGaugeChart.highchartsRenderer.CHART_TYPES.GAUGE_CHART_CATEGORIES_SUMMARY),
105
+ options
106
+ );
107
+ };
108
+
109
+ this.prepareSegments = function (options) {
110
+ let segmentsSumAggregator = 0;
111
+ let segmentsOutput = [];
112
+ for (let i = 0; i < options.segments.length; i++) {
113
+ let currentSegment = options.segments[i];
114
+ segmentsOutput.push(
115
+ {
116
+ from: segmentsSumAggregator,
117
+ to: segmentsSumAggregator += currentSegment.count,
118
+ color: currentSegment.color,
119
+ name: currentSegment.title,
120
+ }
121
+ );
122
+ }
123
+ segmentsOutput = segmentsOutput.filter(
124
+ (segment) => {
125
+ return segment.from !== segment.to;
126
+ }
127
+ );
128
+ options.segments = segmentsOutput;
129
+ options.goal = {
130
+ title: '',
131
+ value: segmentsSumAggregator,
132
+ };
133
+ }
134
+
135
+ this.getCompletionPercentage = function (options) {
136
+ const countOfCompletedItems = options.segments.find(segment => !!segment.isCompletionParameter)?.count || 0;
137
+ const totalCount = options.segments.reduce((acc, segment) => acc + segment.count, 0);
138
+ if (totalCount === 0) {
139
+ return 0;
140
+ }
141
+ return Math.round(countOfCompletedItems / totalCount * 100);
142
+ }
143
+
144
+ this.clampValueToPane = function (value, max = this.max, min = this.min) {
145
+ const correction = Math.abs(max - min) * 0.02;
146
+ return helpers.clamp(min - correction, value, max + correction);
147
+ };
148
+
149
+
150
+ this.formatValueLabel = function (value, options) {
151
+ return `<span style="display: flex; flex-direction: column; align-items: center; gap: 6px; font-size: ${
152
+ options.label.font_size
153
+ }px;">
154
+ <span style="font-weight: 600; font-size: 1.5em; line-height: 1; color: ${options.label.font_color}">
155
+ ${this.completionPercentage}%
156
+ </span>
157
+ <span style="font-weight: 600; font-size: 0.75em; line-height: 1; color: ${
158
+ options.gauge.colors.meta
159
+ };">Completed</span>
160
+ </span>`;
161
+ };
162
+
163
+ this.clampValueToPane = function (value, max = this.max, min = this.min) {
164
+ const correction = Math.abs(max - min) * 0.02;
165
+ return helpers.clamp(min - correction, value, max + correction);
166
+ };
167
+
168
+ this.createValueLabel = function (chart, options) {
169
+ const label = chart.renderer.text(this.formatValueLabel(this.value, options), 0, 0, true).add().toFront();
170
+
171
+ helpers.removeSVGTextCorrection(label, "yCorr");
172
+
173
+ label.attr(DrGaugeChart.getValueLabelPosition(chart, options)).css({
174
+ transform: "translateX(-50%) translateY(-100%)",
175
+ });
176
+
177
+ return label;
178
+ };
179
+
180
+
181
+ this.getPaneDimensions = function (chart, options) {
182
+ const {renderer} = chart;
183
+ const {offset} = options.gauge;
184
+
185
+ const offsetBottom = options.gauge.valueOffset[0] + options.gauge.valueOffset[2] + offset[2];
186
+
187
+ const radiuses = [chart.chartWidth / 2 - Math.max(offset[1], offset[3]), chart.chartHeight - offsetBottom - offset[0]];
188
+
189
+ radiuses.push((chart.chartWidth / 2 - offset[1]) / Math.sin(Math.abs(Math.PI / 2)));
190
+ radiuses.push((chart.chartHeight - offsetBottom - offset[0]) / Math.cos(Math.abs(Math.PI / 2)));
191
+
192
+ return {
193
+ radius: Math.min(...radiuses),
194
+ center: [chart.chartWidth / 2, chart.chartHeight - offsetBottom],
195
+ };
196
+ };
197
+
198
+ this.setPane = function (chart, options) {
199
+ const {radius, center} = this.getPaneDimensions(chart, options);
200
+ chart.pane[0].options.size = 2 * radius;
201
+ chart.pane[0].options.center = center;
202
+ chart.yAxis[0].options.plotBands.forEach((band) => {
203
+ band.outerRadius = radius - (options.gauge.tickLength - options.gauge.thickness) / 2;
204
+ });
205
+ };
206
+
207
+ this.setCustomElements = function (chart, options) {
208
+ chart.label = this.createValueLabel(chart, options);
209
+ chart.startBorder = DrGaugeChart.createBorder(chart, options, "start");
210
+ chart.endBorder = DrGaugeChart.createBorder(chart, options, "end");
211
+ };
212
+
213
+ this.updateCustomElements = function (chart, options) {
214
+ chart.startBorder.attr(DrGaugeChart.getBorderPosition(chart, options, "start"));
215
+ chart.endBorder.attr(DrGaugeChart.getBorderPosition(chart, options, "end"));
216
+ chart.label.attr(DrGaugeChart.getValueLabelPosition(chart, options));
217
+ };
218
+
219
+ this.moveCustomElementsToFront = function (chart) {
220
+ chart.startBorder.toFront();
221
+ chart.endBorder.toFront();
222
+ };
223
+
224
+ this.configChart = function () {
225
+ return {
226
+ title: {
227
+ text: null,
228
+ },
229
+ subtitle: null,
230
+ exporting: {
231
+ allowHTML: true,
232
+ },
233
+ tooltip: {
234
+ enabled: false,
235
+ },
236
+ chart: {
237
+ type: "gauge",
238
+ backgroundColor: this.options.gauge.background,
239
+ plotBackgroundColor: null,
240
+ plotBackgroundImage: null,
241
+ plotBorderWidth: 0,
242
+ plotShadow: false,
243
+ events: {
244
+ render: ({target: chart}) => {
245
+ this.moveCustomElementsToFront(chart);
246
+ },
247
+ beforeRedraw: ({target: chart}) => {
248
+ this.setPane(chart, this.options);
249
+ this.updateCustomElements(chart, this.options);
250
+ },
251
+ beforeRender: ({target: chart}) => {
252
+ this.setPane(chart, this.options);
253
+ this.setCustomElements(chart, this.options);
254
+ }
255
+ },
256
+ margin: [0, 0, 0, 0],
257
+ spacing: [0, 0, 0, 0],
258
+ },
259
+
260
+ pane: {
261
+ startAngle: -90,
262
+ endAngle: 90,
263
+ background: null,
264
+ center: [0, 0],
265
+ },
266
+
267
+ // the value axis
268
+ yAxis: {
269
+ min: this.min,
270
+ max: this.max,
271
+ tickPositions: this.ticks,
272
+ tickPosition: "inside",
273
+ tickColor: this.options.gauge.background,
274
+ tickLength: this.options.gauge.tickLength,
275
+ tickWidth: this.options.gauge.tickWidth,
276
+ minorTickInterval: null,
277
+ labels: {
278
+ enabled: false
279
+ },
280
+ lineWidth: 0,
281
+ plotBands: this.plotBands,
282
+ },
283
+
284
+ series: [
285
+ {
286
+ name: null,
287
+ data: [this.clampValueToPane(this.value)],
288
+ dataLabels: [
289
+ {
290
+ enabled: false,
291
+ },
292
+ ],
293
+ dial: {
294
+ radius: "0",
295
+ backgroundColor: "rgba(0, 0, 0, 0)",
296
+ baseWidth: 2 * this.options.gauge.pivot.radius,
297
+ baseLength: "0%",
298
+ rearLength: "0%",
299
+ },
300
+ pivot: {
301
+ backgroundColor: "rgba(0, 0, 0, 0)",
302
+ radius: this.options.gauge.pivot.radius,
303
+ },
304
+ },
305
+ ],
306
+ };
307
+ };
308
+
309
+
310
+ this.options = this.mergeOptions(opts.chartOptions);
311
+ this.completionPercentage = this.getCompletionPercentage(this.options);
312
+ this.prepareSegments(this.options);
313
+ this.aggregation = pivotData.getAggregator([], []);
314
+ this.format = this.aggregation.widget_values_format;
315
+ this.goal = this.options.goal;
316
+ this.plotBands = this.createPlotBands(this.options);
317
+ this.ticks = DrGaugeChart.createTicks(this.plotBands, this.options);
318
+ this.value = DrGaugeChart.getValue(pivotData, opts);
319
+ this.max = this.ticks[this.ticks.length - 1];
320
+ this.min = this.ticks[0];
321
+ }
322
+
323
+ module.exports = {DrGaugeCategoriesSummaryChart, GAUGE_CATEGORIES_SUMMARY_OPTIONS_DEFAULT};
@@ -84,34 +84,7 @@ function DrGaugeChart(pivotData, opts) {
84
84
  return (value - min) < (max - min) / 2;
85
85
  };
86
86
 
87
- this.createPlotBands = function (options) {
88
- const {
89
- segments,
90
- isAbsoluteValue,
91
- goal: { value: goalValue },
92
- gauge: { thickness },
93
- } = options;
94
-
95
- const bands = segments.map((item, index) => {
96
- const itemFrom = !!index ? item.from - 1 : item.from;
97
- return {
98
- from: isAbsoluteValue ? itemFrom : (itemFrom * goalValue) / 100,
99
- to: isAbsoluteValue ? item.to : (item.to * goalValue) / 100,
100
- color: item.color,
101
- thickness: thickness,
102
- title: item.title,
103
- };
104
- });
105
-
106
- // clamp last segment
107
- bands[bands.length - 1].to = Math.max(bands[bands.length - 1].to, goalValue);
108
-
109
- return bands;
110
- };
111
-
112
- this.createTicks = function (plotBands, options) {
113
- return _.uniq([plotBands[0].from || 0, ...plotBands.map((b) => b.to), options.goal.value]).sort((a, b) => a - b);
114
- };
87
+ this.createTicks = DrGaugeChart.createTicks;
115
88
 
116
89
  this.mergeOptions = function (options) {
117
90
  return helpers.mergeDeep(
@@ -142,21 +115,17 @@ function DrGaugeChart(pivotData, opts) {
142
115
  };
143
116
 
144
117
  this.formatValueLabel = function (value, options) {
145
- return `<span style="display: flex; flex-direction: column; align-items: center; gap: 6px; font-size: ${
146
- options.label.font_size
147
- }px;">
118
+ return `<span style="display: flex; flex-direction: column; align-items: center; gap: 6px; font-size: ${options.label.font_size
119
+ }px;">
148
120
  <span style="font-weight: 600; font-size: 1.3em; line-height: 1; color: ${options.label.font_color}">
149
121
  ${this.formatValue(value)}
150
- ${
151
- options.label.show_percentage_in_value
152
- ? `<span style="font-size: 0.5833em; font-weight: 600; color: ${
153
- options.gauge.colors.meta
154
- };">(${this.toPercent(value)})</span>`
155
- : ""
156
- }
122
+ ${options.label.show_percentage_in_value
123
+ ? `<span style="font-size: 0.5833em; font-weight: 600; color: ${options.gauge.colors.meta
124
+ };">(${this.toPercent(value)})</span>`
125
+ : ""
126
+ }
157
127
  </span>
158
- <span style="font-weight: 500; font-size: 0.875em; line-height: 1; color: ${
159
- options.gauge.colors.meta
128
+ <span style="font-weight: 500; font-size: 0.875em; line-height: 1; color: ${options.gauge.colors.meta
160
129
  };">Current status</span>
161
130
  </span>`;
162
131
  };
@@ -176,8 +145,8 @@ function DrGaugeChart(pivotData, opts) {
176
145
  : "";
177
146
  const percentage = options.label.show_percentage_in_segments
178
147
  ? `<span style="font-size: 0.75em; color: ${options.gauge.colors.meta}; font-weight: 400;">(${this.toPercent(
179
- value
180
- )})</span>`
148
+ value
149
+ )})</span>`
181
150
  : "";
182
151
 
183
152
  return `<span style="
@@ -194,67 +163,6 @@ function DrGaugeChart(pivotData, opts) {
194
163
  </span>`;
195
164
  };
196
165
 
197
- this.getValue = function (pivotData, opts) {
198
- const lineSeries = this.ptCreateBasicLineSeries(pivotData, null, true, null, null, opts, {});
199
-
200
- let total = _.flatten(lineSeries
201
- .map((s) => s.data.map((v) => v))
202
- .filter((v) => !_.isNaN(v))
203
- );
204
-
205
- let aggfunc = (a, b) => a + b;
206
- let base = 0;
207
- let singleValueAgg = this.getSingleValueAgg(
208
- {
209
- value: {
210
- value: "Sum",
211
- },
212
- },
213
- aggfunc,
214
- base
215
- );
216
-
217
- aggfunc = singleValueAgg.aggfunc;
218
- base = singleValueAgg.base;
219
-
220
- const aggregator = pivotData.getAggregator([], []);
221
- const value = total.length > 0 ? total.reduce(aggfunc, base) : aggregator.value();
222
-
223
- return value;
224
- };
225
-
226
- this.getBorderPosition = function (chart, options, position) {
227
- const { center, size } = chart.pane[0].options;
228
- const {
229
- gauge: { tickLength, tickWidth },
230
- } = options;
231
-
232
- return {
233
- x: position === "start" ? center[0] - size / 2 + tickLength / 2 : center[0] + size / 2 - tickLength / 2,
234
- y: center[1] + tickWidth / 2,
235
- };
236
- };
237
-
238
- this.createBorder = function (chart, options, position) {
239
- return chart.renderer
240
- .arc(
241
- Object.assign(this.getBorderPosition(chart, options, position), {
242
- r: options.gauge.thickness / 2,
243
- innerR: 0,
244
- start: 0,
245
- end: Math.PI,
246
- })
247
- )
248
- .attr({
249
- fill:
250
- position === "start"
251
- ? chart.yAxis[0].options.plotBands[0].color
252
- : chart.yAxis[0].options.plotBands.slice(-1)[0].color,
253
- })
254
- .add()
255
- .toFront();
256
- };
257
-
258
166
  this.getGoalIconPosition = function (chart, options) {
259
167
  const { center, size } = chart.pane[0].options;
260
168
  const radius = size / 2;
@@ -277,20 +185,12 @@ function DrGaugeChart(pivotData, opts) {
277
185
  .toFront();
278
186
  };
279
187
 
280
- this.getValueLabelPosition = function (chart, options) {
281
- const { center } = chart.pane[0].options;
282
- return {
283
- x: center[0],
284
- y: center[1] + options.gauge.valueOffset[0],
285
- };
286
- };
287
-
288
188
  this.createValueLabel = function (chart, options) {
289
189
  const label = chart.renderer.text(this.formatValueLabel(this.value, options), 0, 0, true).add().toFront();
290
190
 
291
191
  helpers.removeSVGTextCorrection(label, "yCorr");
292
192
 
293
- label.attr(this.getValueLabelPosition(chart, options)).css({
193
+ label.attr(DrGaugeChart.getValueLabelPosition(chart, options)).css({
294
194
  transform: "translateX(-50%)",
295
195
  });
296
196
 
@@ -314,12 +214,12 @@ function DrGaugeChart(pivotData, opts) {
314
214
  // depends on label width
315
215
  radiuses.push(
316
216
  (chart.chartWidth / 2 - label.bBox.width - Math.max(offset[1], offset[3])) /
317
- Math.sin(Math.abs(Math.PI / 2 - angle))
217
+ Math.sin(Math.abs(Math.PI / 2 - angle))
318
218
  );
319
219
  // depends on label height
320
220
  radiuses.push(
321
221
  (chart.chartHeight - offsetBottom - label.bBox.height / 2 - offset[0]) /
322
- Math.cos(Math.abs(Math.PI / 2 - angle))
222
+ Math.cos(Math.abs(Math.PI / 2 - angle))
323
223
  );
324
224
  label.destroy();
325
225
  });
@@ -361,7 +261,7 @@ function DrGaugeChart(pivotData, opts) {
361
261
 
362
262
  // If goal is hidden, we need to always show goal and hide the closest visible label before it
363
263
  this.updateLabelsWhenGoalIsHidden(ticks, options);
364
-
264
+
365
265
  };
366
266
 
367
267
  this.updateLabelsWhenGoalIsHidden = function (ticks, options) {
@@ -430,8 +330,7 @@ function DrGaugeChart(pivotData, opts) {
430
330
  // goal tooltip if lebels are hidden
431
331
  if (tick.pos === options.goal.value && !options.label.show) {
432
332
  drTooltip.add(
433
- `${
434
- options.label.show_goal_name ? options.goal.title || "" : ""
333
+ `${options.label.show_goal_name ? options.goal.title || "" : ""
435
334
  }<span style="font-weight: 600">${this.formatValue(options.goal.value)}</span>`,
436
335
  chart.goalIcon.element,
437
336
  {
@@ -461,16 +360,41 @@ function DrGaugeChart(pivotData, opts) {
461
360
 
462
361
  this.setCustomElements = function (chart, options) {
463
362
  chart.label = this.createValueLabel(chart, options);
464
- chart.startBorder = this.createBorder(chart, options, "start");
465
- chart.endBorder = this.createBorder(chart, options, "end");
363
+ chart.startBorder = DrGaugeChart.createBorder(chart, options, "start");
364
+ chart.endBorder = DrGaugeChart.createBorder(chart, options, "end");
466
365
  chart.goalIcon = this.createGoalIcon(chart, options);
467
366
  };
468
367
 
469
368
  this.updateCustomElements = function (chart, options) {
470
- chart.startBorder.attr(this.getBorderPosition(chart, options, "start"));
471
- chart.endBorder.attr(this.getBorderPosition(chart, options, "end"));
369
+ chart.startBorder.attr(DrGaugeChart.getBorderPosition(chart, options, "start"));
370
+ chart.endBorder.attr(DrGaugeChart.getBorderPosition(chart, options, "end"));
472
371
  chart.goalIcon.attr(this.getGoalIconPosition(chart, options));
473
- chart.label.attr(this.getValueLabelPosition(chart, options));
372
+ chart.label.attr(DrGaugeChart.getValueLabelPosition(chart, options));
373
+ };
374
+
375
+ this.createPlotBands = function (options) {
376
+ const {
377
+ segments,
378
+ isAbsoluteValue,
379
+ goal: { value: goalValue },
380
+ gauge: { thickness },
381
+ } = options;
382
+
383
+ const bands = segments.map((item, index) => {
384
+ const itemFrom = !!index ? item.from - 1 : item.from;
385
+ return {
386
+ from: isAbsoluteValue ? itemFrom : (itemFrom * goalValue) / 100,
387
+ to: isAbsoluteValue ? item.to : (item.to * goalValue) / 100,
388
+ color: item.color,
389
+ thickness: thickness,
390
+ title: item.title,
391
+ };
392
+ });
393
+
394
+ // clamp last segment
395
+ bands[bands.length - 1].to = Math.max(bands[bands.length - 1].to, goalValue);
396
+
397
+ return bands;
474
398
  };
475
399
 
476
400
  this.moveCustomElementsToFront = function (chart) {
@@ -522,6 +446,7 @@ function DrGaugeChart(pivotData, opts) {
522
446
  },
523
447
  margin: [0, 0, 0, 0],
524
448
  spacing: [0, 0, 0, 0],
449
+ animation: !DrGaugeChart.highchartsRenderer.chartAnimationsDisabled(),
525
450
  },
526
451
 
527
452
  pane: {
@@ -581,6 +506,7 @@ function DrGaugeChart(pivotData, opts) {
581
506
  backgroundColor: this.options.gauge.pivot.color,
582
507
  radius: this.options.gauge.pivot.radius,
583
508
  },
509
+ animation: !DrGaugeChart.highchartsRenderer.chartAnimationsDisabled(),
584
510
  },
585
511
  ],
586
512
  };
@@ -593,9 +519,98 @@ function DrGaugeChart(pivotData, opts) {
593
519
  this.goal = this.options.goal;
594
520
  this.plotBands = this.createPlotBands(this.options);
595
521
  this.ticks = this.createTicks(this.plotBands, this.options);
596
- this.value = this.getValue(pivotData, opts);
522
+ this.value = DrGaugeChart.getValue(pivotData, opts);
597
523
  this.max = this.ticks[this.ticks.length - 1];
598
524
  this.min = this.ticks[0];
599
525
  }
600
526
 
527
+ DrGaugeChart.createTicks = function (plotBands, options) {
528
+ return _.uniq([plotBands[0].from || 0, ...plotBands.map((b) => b.to), options.goal.value]).sort((a, b) => a - b);
529
+ };
530
+
531
+ DrGaugeChart.getBorderPosition = function (chart, options, position) {
532
+ const { center, size } = chart.pane[0].options;
533
+ const {
534
+ gauge: { tickLength, tickWidth },
535
+ } = options;
536
+
537
+ return {
538
+ x: position === "start" ? center[0] - size / 2 + tickLength / 2 : center[0] + size / 2 - tickLength / 2,
539
+ y: center[1] + tickWidth / 2,
540
+ };
541
+ };
542
+
543
+ DrGaugeChart.createBorder = function (chart, options, position) {
544
+ return chart.renderer
545
+ .arc(
546
+ Object.assign(DrGaugeChart.getBorderPosition(chart, options, position), {
547
+ r: options.gauge.thickness / 2,
548
+ innerR: 0,
549
+ start: 0,
550
+ end: Math.PI,
551
+ })
552
+ )
553
+ .attr({
554
+ fill:
555
+ position === "start"
556
+ ? chart.yAxis[0].options.plotBands[0].color
557
+ : chart.yAxis[0].options.plotBands.slice(-1)[0].color,
558
+ })
559
+ .add()
560
+ .toFront();
561
+ };
562
+
563
+ DrGaugeChart.getValueLabelPosition = function (chart, options) {
564
+ const { center } = chart.pane[0].options;
565
+ return {
566
+ x: center[0],
567
+ y: center[1] + options.gauge.valueOffset[0],
568
+ };
569
+ };
570
+
571
+ DrGaugeChart.ptCreateBasicLineSeries = function (pivotData, colors, onlyNumbers, isUniqueVals, additionOptions, opts, chartOptions) {
572
+ return DrGaugeChart.highchartsRenderer.ptCreateBasicLineSeries(
573
+ pivotData,
574
+ colors,
575
+ onlyNumbers,
576
+ isUniqueVals,
577
+ additionOptions,
578
+ opts,
579
+ chartOptions
580
+ );
581
+ };
582
+
583
+ DrGaugeChart.getSingleValueAgg = function (opts, aggfunc, base) {
584
+ return DrGaugeChart.highchartsRenderer.getSingleValueAgg(opts, aggfunc, base);
585
+ };
586
+
587
+ DrGaugeChart.getValue = function (pivotData, opts) {
588
+ const lineSeries = DrGaugeChart.ptCreateBasicLineSeries(pivotData, null, true, null, null, opts, {});
589
+
590
+ let total = _.flatten(lineSeries
591
+ .map((s) => s.data.map((v) => v))
592
+ .filter((v) => !_.isNaN(v))
593
+ );
594
+
595
+ let aggfunc = (a, b) => a + b;
596
+ let base = 0;
597
+ let singleValueAgg = DrGaugeChart.getSingleValueAgg(
598
+ {
599
+ value: {
600
+ value: "Sum",
601
+ },
602
+ },
603
+ aggfunc,
604
+ base
605
+ );
606
+
607
+ aggfunc = singleValueAgg.aggfunc;
608
+ base = singleValueAgg.base;
609
+
610
+ const aggregator = pivotData.getAggregator([], []);
611
+ const value = total.length > 0 ? total.reduce(aggfunc, base) : aggregator.value();
612
+
613
+ return value;
614
+ };
615
+
601
616
  module.exports = { DrGaugeChart, GAUGE_OPTIONS_DEFAULT };
@@ -2,6 +2,7 @@ const helpers = require('./dr-renderer-helpers');
2
2
  const { DrGaugeChart, GAUGE_OPTIONS_DEFAULT } = require('./charts/dr_gauge_chart');
3
3
  const { DrDonutChart } = require('./charts/dr_donut_chart');
4
4
  const seriesPointStylesHelper= require('./seriesPointStyles-helper');
5
+ const {DrGaugeCategoriesSummaryChart} = require("./charts/dr_gauge_categories_summary_chart");
5
6
 
6
7
  const mobileBrowserRegex = new RegExp([
7
8
  '(android|bb\d+|meego).+mobile|avantgo|bada\/|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od)',
@@ -59,6 +60,7 @@ const CHART_TYPES = {
59
60
  GAUGE_SOLID_CHART: 'gauge-solid-chart',
60
61
  GAUGE_CHART: 'gauge-chart',
61
62
  GAUGE_CHART_ENHANCED: 'gauge-chart-enhanced',
63
+ GAUGE_CHART_CATEGORIES_SUMMARY: 'gauge-chart-categories-summary',
62
64
  KPI_WIDGET: 'kpi-widget',
63
65
  TEXT_WIDGET: 'text-widget',
64
66
  WATERFALL_BREAKDOWN: 'waterfall-chart-breakdown',
@@ -2274,6 +2276,10 @@ let getHighchartsRenderer = function ($, document, Highcharts, default_colors, h
2274
2276
  return new DrGaugeChart(pivotData, opts).render();
2275
2277
  };
2276
2278
 
2279
+ highchartsRenderer.ptRenderGaugeCategoriesSummary = (pivotData, opts) => {
2280
+ return new DrGaugeCategoriesSummaryChart(pivotData, opts).render();
2281
+ };
2282
+
2277
2283
  highchartsRenderer.ptRenderKpi = function (pivotData, opts) {
2278
2284
  var chartOptions = {};
2279
2285
 
@@ -7692,6 +7698,14 @@ let getHighchartsRenderer = function ($, document, Highcharts, default_colors, h
7692
7698
  legendName: 'Data Series',
7693
7699
  startedMessage: 'To get started, drag one field to the value section. Best practice: Drag one field to the filter section, and filter as required.',
7694
7700
  },
7701
+ [highchartsRenderer.CHART_TYPES.GAUGE_CHART_CATEGORIES_SUMMARY]: {
7702
+ name: 'Gauge chart categories summary',
7703
+ label: 'Gauge',
7704
+ title: 'Gauge chart showing summary sections for different categories.',
7705
+ axisName: 'X-Axis',
7706
+ legendName: 'Data Series',
7707
+ startedMessage: '',
7708
+ },
7695
7709
  'kpi-widget': {
7696
7710
  name: 'Kpi ',
7697
7711
  label: 'KPI',
@@ -8085,6 +8099,24 @@ let getHighchartsRenderer = function ($, document, Highcharts, default_colors, h
8085
8099
  },
8086
8100
  ]
8087
8101
  },
8102
+ {
8103
+ type: 'gauge-categories-summary',
8104
+ name: 'Gauge Categories Summary',
8105
+ class: 'google-visualization-charteditor-mini-gauge',
8106
+ subtypes: [
8107
+ {
8108
+ type: highchartsRenderer.CHART_TYPES.GAUGE_CHART_CATEGORIES_SUMMARY,
8109
+ name: highchartsRenderer.chartsTypesInfo[highchartsRenderer.CHART_TYPES.GAUGE_CHART_CATEGORIES_SUMMARY].name,
8110
+ class: 'google-visualization-charteditor-thumbs-gauge-solid',
8111
+ render: highchartsRenderer.ptRenderGaugeCategoriesSummary,
8112
+ suboptions: [
8113
+ highchartsRenderer.suboptions["label_gauge"],
8114
+ highchartsRenderer.suboptions["tooltips_gauge"],
8115
+ highchartsRenderer.suboptions["subtitle"]
8116
+ ]
8117
+ },
8118
+ ]
8119
+ },
8088
8120
  {
8089
8121
  type: 'kpi',
8090
8122
  name: 'KPI',
@@ -9900,6 +9932,10 @@ let getHighchartsRenderer = function ($, document, Highcharts, default_colors, h
9900
9932
  disableAnimation = disabled;
9901
9933
  };
9902
9934
 
9935
+ highchartsRenderer.chartAnimationsDisabled = function () {
9936
+ return disableAnimation;
9937
+ };
9938
+
9903
9939
  highchartsRenderer.adjustFormatToSupportedByMoment = function(format) {
9904
9940
  return typeof format === 'string'
9905
9941
  ? format.replace(/y/g, 'Y').replace(/d/g, 'D').replace(/h/g, 'H')
@@ -5,6 +5,7 @@ const { DrChartTooltip } = require("../src/dr_chart_tooltip");
5
5
  jest.mock("../src/dr_chart_tooltip"); // Mock the tooltip class
6
6
 
7
7
  const mockFormattedValue = "16,549";
8
+ let disableChartAnimation = false;
8
9
 
9
10
  DrGaugeChart.highchartsRenderer = {
10
11
  CHART_TYPES: {
@@ -20,6 +21,8 @@ DrGaugeChart.highchartsRenderer = {
20
21
  formatValue: jest.fn().mockReturnValue({
21
22
  value: mockFormattedValue,
22
23
  }),
24
+ disableChartAnimations: jest.fn((value) => disableChartAnimation = value),
25
+ chartAnimationsDisabled: jest.fn(() => disableChartAnimation),
23
26
  };
24
27
 
25
28
  const mockAggregationValue = 1000;
@@ -111,6 +114,11 @@ describe("DrGaugeChart", () => {
111
114
  chart.render();
112
115
  expect(chart.configChart).toHaveBeenCalled();
113
116
  });
117
+
118
+ it("disables chart animations by demand", () => {
119
+ chart.render();
120
+ expect(DrGaugeChart.highchartsRenderer.chartAnimationsDisabled).toHaveBeenCalledTimes(2);
121
+ });
114
122
  });
115
123
 
116
124
  describe("isLeftQuarter", () => {
@@ -306,7 +314,7 @@ describe("DrGaugeChart", () => {
306
314
  describe("createTicks", () => {
307
315
  it("should create a ticks array", () => {
308
316
  expect(
309
- chart.createTicks(
317
+ DrGaugeChart.createTicks(
310
318
  [
311
319
  {
312
320
  to: 100,
@@ -329,7 +337,7 @@ describe("DrGaugeChart", () => {
329
337
 
330
338
  it("should sort and remove duplicates", () => {
331
339
  expect(
332
- chart.createTicks(
340
+ DrGaugeChart.createTicks(
333
341
  [
334
342
  {
335
343
  to: 100,
@@ -707,12 +715,12 @@ describe("DrGaugeChart", () => {
707
715
  describe("getValue", () => {
708
716
  it("should return aggregator value unledss the total is empty", () => {
709
717
  DrGaugeChart.highchartsRenderer.ptCreateBasicLineSeries.mockReturnValue([]);
710
- expect(chart.getValue(mockPivotData, {})).toBe(mockAggregationValue);
718
+ expect(DrGaugeChart.getValue(mockPivotData, {})).toBe(mockAggregationValue);
711
719
  });
712
720
 
713
721
  it("should return value to display", () => {
714
722
  DrGaugeChart.highchartsRenderer.ptCreateBasicLineSeries.mockReturnValue([{ data: [2000] }]);
715
- expect(chart.getValue(mockPivotData, {})).toBe(2000);
723
+ expect(DrGaugeChart.getValue(mockPivotData, {})).toBe(2000);
716
724
  });
717
725
 
718
726
  it("should calculate total", () => {
@@ -721,7 +729,7 @@ describe("DrGaugeChart", () => {
721
729
  { data: [3000] },
722
730
  { data: [5000] },
723
731
  ]);
724
- expect(chart.getValue(mockPivotData, {})).toBe(10000);
732
+ expect(DrGaugeChart.getValue(mockPivotData, {})).toBe(10000);
725
733
  });
726
734
  });
727
735
 
@@ -745,14 +753,14 @@ describe("DrGaugeChart", () => {
745
753
  };
746
754
 
747
755
  it("return a start border position", () => {
748
- expect(chart.getBorderPosition(mockChart, mockOptions, "start")).toEqual({
756
+ expect(DrGaugeChart.getBorderPosition(mockChart, mockOptions, "start")).toEqual({
749
757
  x: 170,
750
758
  y: 201,
751
759
  });
752
760
  });
753
761
 
754
762
  it("return an end border position", () => {
755
- expect(chart.getBorderPosition(mockChart, mockOptions, "end")).toEqual({
763
+ expect(DrGaugeChart.getBorderPosition(mockChart, mockOptions, "end")).toEqual({
756
764
  x: 230,
757
765
  y: 201,
758
766
  });
@@ -761,7 +769,7 @@ describe("DrGaugeChart", () => {
761
769
 
762
770
  describe("createBorder", () => {
763
771
  beforeEach(() => {
764
- chart.getBorderPosition = jest.fn().mockReturnValue({
772
+ DrGaugeChart.getBorderPosition = jest.fn().mockReturnValue({
765
773
  x: 100,
766
774
  y: 200,
767
775
  });
@@ -805,7 +813,7 @@ describe("DrGaugeChart", () => {
805
813
  };
806
814
 
807
815
  it("should create a start border", () => {
808
- chart.createBorder(
816
+ DrGaugeChart.createBorder(
809
817
  mockChart,
810
818
  {
811
819
  gauge: {
@@ -834,7 +842,7 @@ describe("DrGaugeChart", () => {
834
842
  });
835
843
 
836
844
  it("should create an end border", () => {
837
- chart.createBorder(
845
+ DrGaugeChart.createBorder(
838
846
  mockChart,
839
847
  {
840
848
  gauge: {
@@ -990,7 +998,7 @@ describe("DrGaugeChart", () => {
990
998
  describe("getValueLabelPosition", () => {
991
999
  it("should return a value label position depends on the pane center and offsets", () => {
992
1000
  expect(
993
- chart.getValueLabelPosition(
1001
+ DrGaugeChart.getValueLabelPosition(
994
1002
  {
995
1003
  pane: [
996
1004
  {
@@ -1012,7 +1020,7 @@ describe("DrGaugeChart", () => {
1012
1020
  });
1013
1021
 
1014
1022
  expect(
1015
- chart.getValueLabelPosition(
1023
+ DrGaugeChart.getValueLabelPosition(
1016
1024
  {
1017
1025
  pane: [
1018
1026
  {
@@ -1044,7 +1052,7 @@ describe("DrGaugeChart", () => {
1044
1052
 
1045
1053
  beforeEach(() => {
1046
1054
  chart.formatValueLabel = jest.fn().mockReturnValue("<div>Mock label</div>");
1047
- chart.getValueLabelPosition = jest.fn().mockReturnValue({
1055
+ DrGaugeChart.getValueLabelPosition = jest.fn().mockReturnValue({
1048
1056
  x: 100,
1049
1057
  y: 200,
1050
1058
  });
@@ -1540,7 +1548,7 @@ describe("DrGaugeChart", () => {
1540
1548
 
1541
1549
  // Spy on class methods
1542
1550
  jest.spyOn(chart, "createValueLabel").mockReturnValue(mockValueLabel);
1543
- jest.spyOn(chart, "createBorder").mockImplementation((chart, options, position) => {
1551
+ jest.spyOn(DrGaugeChart, "createBorder").mockImplementation((chart, options, position) => {
1544
1552
  if (position === "start") return mockStartBorder;
1545
1553
  if (position === "end") return mockEndBorder;
1546
1554
  });
@@ -1561,14 +1569,14 @@ describe("DrGaugeChart", () => {
1561
1569
  it("assigns the start border to chart.startBorder", () => {
1562
1570
  chart.setCustomElements(mockChart, mockOptions);
1563
1571
 
1564
- expect(chart.createBorder).toHaveBeenCalledWith(mockChart, mockOptions, "start");
1572
+ expect(DrGaugeChart.createBorder).toHaveBeenCalledWith(mockChart, mockOptions, "start");
1565
1573
  expect(mockChart.startBorder).toBe(mockStartBorder);
1566
1574
  });
1567
1575
 
1568
1576
  it("assigns the end border to chart.endBorder", () => {
1569
1577
  chart.setCustomElements(mockChart, mockOptions);
1570
1578
 
1571
- expect(chart.createBorder).toHaveBeenCalledWith(mockChart, mockOptions, "end");
1579
+ expect(DrGaugeChart.createBorder).toHaveBeenCalledWith(mockChart, mockOptions, "end");
1572
1580
  expect(mockChart.endBorder).toBe(mockEndBorder);
1573
1581
  });
1574
1582
 
@@ -1609,12 +1617,12 @@ describe("DrGaugeChart", () => {
1609
1617
  mockValueLabelPosition = { x: 70, y: 80 };
1610
1618
 
1611
1619
  // Spy on helper methods
1612
- jest.spyOn(chart, "getBorderPosition").mockImplementation((chart, options, position) => {
1620
+ jest.spyOn(DrGaugeChart, "getBorderPosition").mockImplementation((chart, options, position) => {
1613
1621
  if (position === "start") return mockStartPosition;
1614
1622
  if (position === "end") return mockEndPosition;
1615
1623
  });
1616
1624
  jest.spyOn(chart, "getGoalIconPosition").mockReturnValue(mockGoalIconPosition);
1617
- jest.spyOn(chart, "getValueLabelPosition").mockReturnValue(mockValueLabelPosition);
1625
+ jest.spyOn(DrGaugeChart, "getValueLabelPosition").mockReturnValue(mockValueLabelPosition);
1618
1626
  });
1619
1627
 
1620
1628
  afterEach(() => {
@@ -1624,14 +1632,14 @@ describe("DrGaugeChart", () => {
1624
1632
  it("updates startBorder attributes correctly", () => {
1625
1633
  chart.updateCustomElements(mockChart, mockOptions);
1626
1634
 
1627
- expect(chart.getBorderPosition).toHaveBeenCalledWith(mockChart, mockOptions, "start");
1635
+ expect(DrGaugeChart.getBorderPosition).toHaveBeenCalledWith(mockChart, mockOptions, "start");
1628
1636
  expect(mockChart.startBorder.attr).toHaveBeenCalledWith(mockStartPosition);
1629
1637
  });
1630
1638
 
1631
1639
  it("updates endBorder attributes correctly", () => {
1632
1640
  chart.updateCustomElements(mockChart, mockOptions);
1633
1641
 
1634
- expect(chart.getBorderPosition).toHaveBeenCalledWith(mockChart, mockOptions, "end");
1642
+ expect(DrGaugeChart.getBorderPosition).toHaveBeenCalledWith(mockChart, mockOptions, "end");
1635
1643
  expect(mockChart.endBorder.attr).toHaveBeenCalledWith(mockEndPosition);
1636
1644
  });
1637
1645
 
@@ -1645,7 +1653,7 @@ describe("DrGaugeChart", () => {
1645
1653
  it("updates label attributes correctly", () => {
1646
1654
  chart.updateCustomElements(mockChart, mockOptions);
1647
1655
 
1648
- expect(chart.getValueLabelPosition).toHaveBeenCalledWith(mockChart, mockOptions);
1656
+ expect(DrGaugeChart.getValueLabelPosition).toHaveBeenCalledWith(mockChart, mockOptions);
1649
1657
  expect(mockChart.label.attr).toHaveBeenCalledWith(mockValueLabelPosition);
1650
1658
  });
1651
1659
  });