@datarailsshared/dr_renderer 1.2.11 → 1.2.12

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.
@@ -1,566 +0,0 @@
1
- const _ = require('lodash');
2
- const { DrChartTooltip } = require("../dr_chart_tooltip");
3
- const helpers = require("../dr-renderer-helpers");
4
-
5
- const GAUGE_OPTIONS_DEFAULT = {
6
- gauge: {
7
- background: "#fff",
8
- startAngle: -90,
9
- endAngle: 90,
10
- thickness: 16,
11
- tickLength: 40,
12
- tickWidth: 2,
13
- valueOffset: [20, 0, 8, 0],
14
- offset: [8, 8, 8, 8],
15
- goalIcon:
16
- "data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNiIgaGVpZ2h0PSIxNSIgdmlld0JveD0iMCAwIDE2IDE1IiBmaWxsPSJub25lIj4KICAgIDxwYXRoIGQ9Ik04IDBMOS43OTYxMSA1LjUyNzg2SDE1LjYwODVMMTAuOTA2MiA4Ljk0NDI3TDEyLjcwMjMgMTQuNDcyMUw4IDExLjA1NTdMMy4yOTc3MiAxNC40NzIxTDUuMDkzODMgOC45NDQyN0wwLjM5MTU0OCA1LjUyNzg2SDYuMjAzODlMOCAwWiIgZmlsbD0iIzQ2NDZDRSIvPgo8L3N2Zz4=",
17
- goalIconSize: [16, 16],
18
- pivot: {
19
- radius: 5,
20
- color: "#808080",
21
- },
22
- colors: {
23
- meta: "#6D6E6F",
24
- goal: "#4646CE",
25
- },
26
- },
27
- goal: {
28
- title: "Goal",
29
- value: 1000000,
30
- },
31
- isAbsoluteValue: false,
32
- segments: [
33
- {
34
- from: 0,
35
- to: 50,
36
- title: "Low",
37
- color: "#BF1D30",
38
- },
39
- {
40
- from: 51,
41
- to: 90,
42
- title: "Medium",
43
- color: "#FFA310",
44
- },
45
- {
46
- from: 91,
47
- to: 100,
48
- title: "High",
49
- color: "#037C5A",
50
- },
51
- ],
52
- };
53
-
54
- function DrGaugeChart(pivotData, opts) {
55
- this.render = function () {
56
- return DrGaugeChart.highchartsRenderer.ptCreateElementAndDraw(this.configChart(), opts);
57
- };
58
-
59
- this.formatValue = function (data_type, number_format, value, widget_values_format) {
60
- return DrGaugeChart.highchartsRenderer.formatValue(data_type, number_format, value, widget_values_format);
61
- };
62
-
63
- this.getDefaultValueForChart = function (type, existing_options) {
64
- return DrGaugeChart.highchartsRenderer.getDefaultValueForChart(type, existing_options);
65
- };
66
-
67
- this.ptCreateBasicLineSeries = function (pivotData, colors, onlyNumbers, isUniqueVals, additionOptions, opts, chartOptions) {
68
- return DrGaugeChart.highchartsRenderer.ptCreateBasicLineSeries(
69
- pivotData,
70
- colors,
71
- onlyNumbers,
72
- isUniqueVals,
73
- additionOptions,
74
- opts,
75
- chartOptions
76
- );
77
- };
78
-
79
- this.getSingleValueAgg = function (opts, aggfunc, base) {
80
- return DrGaugeChart.highchartsRenderer.getSingleValueAgg(opts, aggfunc, base);
81
- };
82
-
83
- this.isLeftQuarter = function (value, max = this.max) {
84
- return value < max / 2;
85
- };
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
- };
115
-
116
- this.mergeOptions = function (options) {
117
- return helpers.mergeDeep(
118
- JSON.parse(JSON.stringify(GAUGE_OPTIONS_DEFAULT)),
119
- this.getDefaultValueForChart(DrGaugeChart.highchartsRenderer.CHART_TYPES.GAUGE_CHART_ENHANCED),
120
- options
121
- );
122
- };
123
-
124
- this.getAngleForValue = function (
125
- value,
126
- min = this.min,
127
- max = this.max,
128
- startAngle = this.options.gauge.startAngle,
129
- endAngle = this.options.gauge.endAngle
130
- ) {
131
- const degrees = ((value - min) / (max - min)) * (endAngle - startAngle);
132
- const radians = degrees * (Math.PI / 180);
133
- return radians;
134
- };
135
-
136
- this.formatValue = function (value, format = this.format) {
137
- return helpers.isNumber(value) ? DrGaugeChart.highchartsRenderer.formatValue("n", format, value).value : value;
138
- };
139
-
140
- this.toPercent = function (value) {
141
- return `${Math.round((value * 100) / (this.options.isAbsoluteValue ? this.max : this.goal.value))}%`;
142
- };
143
-
144
- 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;">
148
- <span style="font-weight: 600; font-size: 1.5em; line-height: 1; color: ${options.label.font_color}">
149
- ${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
- }
157
- </span>
158
- <span style="font-weight: 500; font-size: 0.875em; line-height: 1; color: ${
159
- options.gauge.colors.meta
160
- };">Current status</span>
161
- </span>`;
162
- };
163
-
164
- this.formatTickLabel = function (value, options) {
165
- const isGoal = value === options.goal.value;
166
- const isLeftQuarter = this.isLeftQuarter(value);
167
- const formattedValue = this.formatValue(value);
168
-
169
- const goalStyles = isGoal ? `padding-${isLeftQuarter ? "right" : "left"}: 12px;` : "";
170
- const goalValue = isGoal
171
- ? `<span style="font-size: 1.125em; color: ${options.gauge.colors.goal};">${formattedValue}</span>`
172
- : `<span style="color: ${options.label.font_color};">${formattedValue}</span>`;
173
- const goalTitle =
174
- isGoal && options.label.show_goal_name && options.goal.title
175
- ? `<span style="font-size: 0.75em; color: ${options.gauge.colors.goal};">${options.goal.title}</span>`
176
- : "";
177
- const percentage = options.label.show_percentage_in_segments
178
- ? `<span style="font-size: 0.75em; color: ${options.gauge.colors.meta}; font-weight: 400;">(${this.toPercent(
179
- value
180
- )})</span>`
181
- : "";
182
-
183
- return `<span style="
184
- display: flex;
185
- align-items: center;
186
- gap: 4px;
187
- font-weight: 600;
188
- font-size: ${options.label.font_size}px;
189
- ${goalStyles}
190
- ">
191
- ${goalValue}
192
- ${goalTitle}
193
- ${percentage}
194
- </span>`;
195
- };
196
-
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
- this.getGoalIconPosition = function (chart, options) {
259
- const { center, size } = chart.pane[0].options;
260
- const radius = size / 2;
261
- const {
262
- gauge: { goalIconSize },
263
- goal: { value },
264
- } = options;
265
-
266
- return {
267
- x: center[0] - radius * Math.sin(Math.PI / 2 - this.getAngleForValue(value)) - goalIconSize[0] / 2,
268
- y: center[1] - radius * Math.cos(Math.PI / 2 - this.getAngleForValue(value)) - goalIconSize[1] / 2,
269
- };
270
- };
271
-
272
- this.createGoalIcon = function (chart, options) {
273
- const point = this.getGoalIconPosition(chart, options);
274
- return chart.renderer
275
- .image(options.gauge.goalIcon, point.x, point.y, options.gauge.goalIconSize[0], options.gauge.goalIconSize[1])
276
- .add()
277
- .toFront();
278
- };
279
-
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
- this.createValueLabel = function (chart, options) {
289
- const label = chart.renderer.text(this.formatValueLabel(this.value, options), 0, 0, true).add().toFront();
290
-
291
- helpers.removeSVGTextCorrection(label, "yCorr");
292
-
293
- label.attr(this.getValueLabelPosition(chart, options)).css({
294
- transform: "translateX(-50%)",
295
- });
296
-
297
- return label;
298
- };
299
-
300
- this.getPaneDimensions = function (chart, options) {
301
- const { renderer } = chart;
302
- const valueLabel = this.createValueLabel(chart, this.options);
303
- const { offset } = options.gauge;
304
- const { height: labelH } = valueLabel.getBBox();
305
-
306
- const offsetBottom = labelH + options.gauge.valueOffset[0] + options.gauge.valueOffset[2] + offset[2];
307
- valueLabel.destroy();
308
-
309
- const radiuses = [chart.chartWidth / 2 - Math.max(offset[1], offset[3]), chart.chartHeight - offsetBottom - offset[0]];
310
- if (options.label.show) {
311
- this.ticks.forEach((tick) => {
312
- const label = renderer.label(this.formatTickLabel(tick, options), 0, 0, null, null, null, true).add();
313
- const angle = this.getAngleForValue(tick);
314
- // depends on label width
315
- radiuses.push(
316
- (chart.chartWidth / 2 - label.bBox.width - Math.max(offset[1], offset[3])) /
317
- Math.sin(Math.abs(Math.PI / 2 - angle))
318
- );
319
- // depends on label height
320
- radiuses.push(
321
- (chart.chartHeight - offsetBottom - label.bBox.height / 2 - offset[0]) /
322
- Math.cos(Math.abs(Math.PI / 2 - angle))
323
- );
324
- label.destroy();
325
- });
326
- } else {
327
- // reserve space for the goal icon
328
- const angle = this.getAngleForValue(options.goal.value);
329
- const [iconW, iconH] = options.gauge.goalIconSize;
330
- radiuses.push((chart.chartWidth / 2 - iconW / 2 - offset[1]) / Math.sin(Math.abs(Math.PI / 2 - angle)));
331
- radiuses.push((chart.chartHeight - offsetBottom - iconH / 2 - offset[0]) / Math.cos(Math.abs(Math.PI / 2 - angle)));
332
- }
333
-
334
- return {
335
- radius: Math.min(...radiuses),
336
- center: [chart.chartWidth / 2, chart.chartHeight - offsetBottom],
337
- };
338
- };
339
-
340
- this.setTicksStyles = function (chart, options) {
341
- Object.keys(chart.yAxis[0].ticks).forEach((i) => {
342
- const tick = chart.yAxis[0].ticks[i];
343
- const isLeftQuarter = this.isLeftQuarter(tick.pos);
344
-
345
- if (tick.pos === options.goal.value) {
346
- tick.mark.attr({
347
- stroke: options.gauge.colors.goal,
348
- "stroke-dasharray": 2,
349
- "pointer-events": "none",
350
- });
351
- }
352
-
353
- if (!tick.label) return;
354
-
355
- // align left querter's labels
356
- tick.label.css({
357
- transform: `translate(${isLeftQuarter ? "-100%" : 0}, 0)`,
358
- });
359
- });
360
- };
361
-
362
- this.addTooltips = function (chart, options) {
363
- if (!options.tooltips.show) {
364
- return false;
365
- }
366
-
367
- const drTooltip = new DrChartTooltip(chart, {
368
- fontSize: options.tooltips.font_size,
369
- fontFamily: options.tooltips.font_style,
370
- color: options.tooltips.font_color,
371
- });
372
-
373
- // segment title tooltip
374
- if (options.tooltips.show_segment_name) {
375
- chart.yAxis[0].plotLinesAndBands.forEach((band, i) => {
376
- drTooltip.add(band.options.title, band.svgElem.element, {
377
- direction: "top",
378
- followPointer: true,
379
- });
380
- });
381
- }
382
-
383
- // value label tooltip
384
- if (options.tooltips.show_percentage_in_value && !options.label.show_percentage_in_value) {
385
- drTooltip.add(this.toPercent(this.value), chart.label.element, {
386
- direction: "top",
387
- });
388
- }
389
-
390
- // segment name tooltips
391
- Object.keys(chart.yAxis[0].ticks).forEach((i) => {
392
- const tick = chart.yAxis[0].ticks[i];
393
- const isLeftQuarter = this.isLeftQuarter(tick.pos);
394
-
395
- // goal tooltip if lebels are hidden
396
- if (tick.pos === options.goal.value && !options.label.show) {
397
- drTooltip.add(
398
- `${
399
- options.label.show_goal_name ? options.goal.title || "" : ""
400
- }<span style="font-weight: 600">${this.formatValue(options.goal.value)}</span>`,
401
- chart.goalIcon.element,
402
- {
403
- direction: isLeftQuarter ? "left" : "right",
404
- }
405
- );
406
- }
407
-
408
- // segment label percentage tooltips
409
- if (tick.label && options.tooltips.show_percentage_in_segments && !options.label.show_percentage_in_segments) {
410
- drTooltip.add(this.toPercent(tick.pos), tick.label.element, {
411
- direction: isLeftQuarter ? "left" : "right",
412
- });
413
- }
414
- });
415
- };
416
-
417
- this.setPane = function (chart, options) {
418
- const { radius, center } = this.getPaneDimensions(chart, options);
419
- chart.pane[0].options.size = 2 * radius;
420
- chart.pane[0].options.center = center;
421
- chart.yAxis[0].options.plotBands.forEach((band) => {
422
- band.outerRadius = radius - (options.gauge.tickLength - options.gauge.thickness) / 2;
423
- });
424
- chart.series[0].options.dial.radius = Math.round((100 * (radius - options.gauge.tickLength - 10)) / radius) + "%";
425
- };
426
-
427
- this.setCustomElements = function (chart, options) {
428
- chart.label = this.createValueLabel(chart, options);
429
- chart.startBorder = this.createBorder(chart, options, "start");
430
- chart.endBorder = this.createBorder(chart, options, "end");
431
- chart.goalIcon = this.createGoalIcon(chart, options);
432
- };
433
-
434
- this.updateCustomElements = function (chart, options) {
435
- chart.startBorder.attr(this.getBorderPosition(chart, options, "start"));
436
- chart.endBorder.attr(this.getBorderPosition(chart, options, "end"));
437
- chart.goalIcon.attr(this.getGoalIconPosition(chart, options));
438
- chart.label.attr(this.getValueLabelPosition(chart, options));
439
- };
440
-
441
- this.moveCustomElementsToFront = function (chart) {
442
- chart.startBorder.toFront();
443
- chart.endBorder.toFront();
444
- chart.goalIcon.toFront();
445
- };
446
-
447
- this.clampValueToPane = function (value, max = this.max, min = this.min) {
448
- const correction = Math.abs(max - min) * 0.02;
449
- return helpers.clamp(min - correction, value, max + correction);
450
- };
451
-
452
- this.configChart = function () {
453
- return {
454
- title: {
455
- text: null,
456
- },
457
- subtitle: null,
458
- exporting: {
459
- allowHTML: true,
460
- },
461
- tooltip: {
462
- enabled: false,
463
- },
464
- chart: {
465
- type: "gauge",
466
- backgroundColor: this.options.gauge.background,
467
- plotBackgroundColor: null,
468
- plotBackgroundImage: null,
469
- plotBorderWidth: 0,
470
- plotShadow: false,
471
- events: {
472
- render: ({ target: chart }) => {
473
- this.moveCustomElementsToFront(chart);
474
- this.setTicksStyles(chart, this.options);
475
- },
476
- beforeRedraw: ({ target: chart }) => {
477
- this.setPane(chart, this.options);
478
- this.updateCustomElements(chart, this.options);
479
- },
480
- beforeRender: ({ target: chart }) => {
481
- this.setPane(chart, this.options);
482
- this.setCustomElements(chart, this.options);
483
- },
484
- load: ({ target: chart }) => {
485
- this.addTooltips(chart, this.options);
486
- },
487
- },
488
- margin: [0, 0, 0, 0],
489
- spacing: [0, 0, 0, 0],
490
- },
491
-
492
- pane: {
493
- startAngle: -90,
494
- endAngle: 90,
495
- background: null,
496
- center: [0, 0],
497
- },
498
-
499
- // the value axis
500
- yAxis: {
501
- min: this.min,
502
- max: this.max,
503
- tickPositions: this.ticks,
504
- tickPosition: "inside",
505
- tickColor: this.options.gauge.background,
506
- tickLength: this.options.gauge.tickLength,
507
- tickWidth: this.options.gauge.tickWidth,
508
- minorTickInterval: null,
509
-
510
- labels: {
511
- enabled: !!this.options.label.show,
512
- distance: 0,
513
- verticalAlign: "middle",
514
- allowOverlap: true,
515
- align: "left",
516
- style: {
517
- whiteSpace: "nowrap",
518
- width: "auto",
519
- },
520
- formatter: ({ value }) => {
521
- return this.formatTickLabel(value, this.options);
522
- },
523
- useHTML: true,
524
- },
525
- lineWidth: 0,
526
- plotBands: this.plotBands,
527
- },
528
-
529
- series: [
530
- {
531
- name: null,
532
- data: [this.clampValueToPane(this.value)],
533
- dataLabels: [
534
- {
535
- enabled: false,
536
- },
537
- ],
538
- dial: {
539
- radius: "70%",
540
- backgroundColor: this.options.gauge.pivot.color,
541
- baseWidth: 2 * this.options.gauge.pivot.radius,
542
- baseLength: "0%",
543
- rearLength: "0%",
544
- },
545
- pivot: {
546
- backgroundColor: this.options.gauge.pivot.color,
547
- radius: this.options.gauge.pivot.radius,
548
- },
549
- },
550
- ],
551
- };
552
- };
553
-
554
- this.originalOptions = opts;
555
- this.options = this.mergeOptions(opts.chartOptions);
556
- this.aggregation = pivotData.getAggregator([], []);
557
- this.format = this.aggregation.widget_values_format;
558
- this.goal = this.options.goal;
559
- this.plotBands = this.createPlotBands(this.options);
560
- this.ticks = this.createTicks(this.plotBands, this.options);
561
- this.value = this.getValue(pivotData, opts);
562
- this.max = this.ticks[this.ticks.length - 1];
563
- this.min = this.ticks[0];
564
- }
565
-
566
- module.exports = { DrGaugeChart, GAUGE_OPTIONS_DEFAULT };
@@ -1,13 +0,0 @@
1
- declare interface IFormatValueResult {
2
- value: string,
3
- align?: 'right' | '',
4
- color?: 'Red' | 'Green' | 'White' | 'Blue' | 'Magenta' | 'Yellow' | 'Cyan' | 'Black' | '',
5
- pattern: string
6
- }
7
-
8
- declare class DataFormatterImpl {
9
- formatValue(type: string, format: string, value: number, overrideFormat?: string): IFormatValueResult
10
- }
11
-
12
- declare const DataFormatter: DataFormatterImpl;
13
- export = DataFormatter;
@@ -1,58 +0,0 @@
1
- function backendSortingKeysAreNotEmpty(keys) {
2
- return !!keys && (!!keys.row_keys && !!keys.row_keys.length || !!keys.col_keys && !!keys.col_keys.length);
3
- }
4
-
5
- function capitalize(string) {
6
- if (typeof string !== 'string') return '';
7
- return string.charAt(0).toUpperCase() + string.slice(1);
8
- }
9
-
10
- function clamp(min, v, max) {
11
- return Math.min(max, Math.max(v, min));
12
- }
13
-
14
- function isNumber(n) {
15
- return !isNaN(parseFloat(n)) && isFinite(n);
16
- }
17
-
18
- function mergeDeep(target, ...sources) {
19
- const isObject = (obj) => obj && typeof obj === 'object' && !Array.isArray(obj);
20
-
21
- if (!isObject(target)) return target;
22
-
23
- sources.forEach((source) => {
24
- if (!isObject(source)) return;
25
-
26
- Object.keys(source).forEach((key) => {
27
- const targetValue = target[key];
28
- const sourceValue = source[key];
29
-
30
- if (isObject(targetValue) && isObject(sourceValue)) {
31
- target[key] = mergeDeep(Object.assign({}, targetValue), sourceValue);
32
- } else {
33
- target[key] = sourceValue;
34
- }
35
- });
36
- });
37
-
38
- return target;
39
- }
40
-
41
- function removeSVGTextCorrection(svgEl, corr = 'yCorr') {
42
- Object.defineProperty(svgEl, corr, {
43
- set: function() {},
44
- get: function() {
45
- return 0;
46
- }
47
- });
48
- return svgEl;
49
- }
50
-
51
- module.exports = {
52
- backendSortingKeysAreNotEmpty,
53
- capitalize,
54
- clamp,
55
- isNumber,
56
- mergeDeep,
57
- removeSVGTextCorrection,
58
- }