@nyaruka/temba-components 0.124.2 → 0.124.3
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/CHANGELOG.md +7 -0
- package/demo/chart/example.html +346 -26
- package/demo/data/server/opened-tickets.json +15 -3
- package/dist/temba-components.js +241 -241
- package/dist/temba-components.js.map +1 -1
- package/out-tsc/src/chart/TembaChart.js +296 -30
- package/out-tsc/src/chart/TembaChart.js.map +1 -1
- package/out-tsc/src/store/Store.js +1 -0
- package/out-tsc/src/store/Store.js.map +1 -1
- package/out-tsc/src/utils/index.js +37 -0
- package/out-tsc/src/utils/index.js.map +1 -1
- package/package.json +1 -1
- package/src/chart/TembaChart.ts +306 -36
- package/src/store/Store.ts +1 -0
- package/src/utils/index.ts +40 -0
package/src/chart/TembaChart.ts
CHANGED
|
@@ -3,33 +3,223 @@ import { property, state } from 'lit/decorators.js';
|
|
|
3
3
|
import { css, html, PropertyValueMap, TemplateResult } from 'lit';
|
|
4
4
|
|
|
5
5
|
import { Select, SelectOption } from '../select/Select';
|
|
6
|
-
import { getClasses } from '../utils';
|
|
6
|
+
import { darkenColor, getClasses } from '../utils';
|
|
7
7
|
import { getStore } from '../store/Store';
|
|
8
8
|
|
|
9
9
|
// eslint-disable-next-line import/no-named-as-default
|
|
10
10
|
import Chart, { ChartType } from 'chart.js/auto';
|
|
11
11
|
import 'chartjs-adapter-luxon';
|
|
12
12
|
|
|
13
|
-
const
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
'
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
'
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
13
|
+
const DEFAULT_PALETTE: keyof typeof COLOR_PALETTES = 'qualitative-set1';
|
|
14
|
+
const COLOR_PALETTES = {
|
|
15
|
+
// Qualitative (categorical, no order)
|
|
16
|
+
'qualitative-set1': [
|
|
17
|
+
'#5ea3db',
|
|
18
|
+
'#c186e3',
|
|
19
|
+
'#66c2a5',
|
|
20
|
+
'#fc8d62',
|
|
21
|
+
'#a6d854',
|
|
22
|
+
'#ffd92f',
|
|
23
|
+
'#e5c494',
|
|
24
|
+
'#b3b3b3'
|
|
25
|
+
],
|
|
26
|
+
'qualitative-set2': [
|
|
27
|
+
'#377eb8',
|
|
28
|
+
'#984ea3',
|
|
29
|
+
'#4daf4a',
|
|
30
|
+
'#ff7f00',
|
|
31
|
+
'#e41a1c',
|
|
32
|
+
'#a65628',
|
|
33
|
+
'#f781bf',
|
|
34
|
+
'#ffff33'
|
|
35
|
+
],
|
|
36
|
+
'qualitative-set3': [
|
|
37
|
+
'#1b9e77',
|
|
38
|
+
'#d95f02',
|
|
39
|
+
'#7570b3',
|
|
40
|
+
'#e7298a',
|
|
41
|
+
'#66a61e',
|
|
42
|
+
'#e6ab02',
|
|
43
|
+
'#a6761d'
|
|
44
|
+
],
|
|
45
|
+
'qualitative-paired': [
|
|
46
|
+
'#1f78b4',
|
|
47
|
+
'#a6cee3',
|
|
48
|
+
'#6a3d9a',
|
|
49
|
+
'#cab2d6',
|
|
50
|
+
'#33a02c',
|
|
51
|
+
'#b2df8a',
|
|
52
|
+
'#e31a1c',
|
|
53
|
+
'#fb9a99',
|
|
54
|
+
'#ff7f00',
|
|
55
|
+
'#fdbf6f'
|
|
56
|
+
],
|
|
57
|
+
'qualitative-accent': [
|
|
58
|
+
'#7fc97f',
|
|
59
|
+
'#beaed4',
|
|
60
|
+
'#fdc086',
|
|
61
|
+
'#ffff99',
|
|
62
|
+
'#386cb0',
|
|
63
|
+
'#f0027f',
|
|
64
|
+
'#bf5b17',
|
|
65
|
+
'#666666'
|
|
66
|
+
],
|
|
67
|
+
'qualitative-pastel1': [
|
|
68
|
+
'#fbb4ae',
|
|
69
|
+
'#b3cde3',
|
|
70
|
+
'#ccebc5',
|
|
71
|
+
'#decbe4',
|
|
72
|
+
'#fed9a6',
|
|
73
|
+
'#ffffcc',
|
|
74
|
+
'#e5d8bd',
|
|
75
|
+
'#fddaec'
|
|
76
|
+
],
|
|
77
|
+
'qualitative-pastel2': [
|
|
78
|
+
'#b3e2cd',
|
|
79
|
+
'#fdcdac',
|
|
80
|
+
'#cbd5e8',
|
|
81
|
+
'#f4cae4',
|
|
82
|
+
'#e6f5c9',
|
|
83
|
+
'#fff2ae',
|
|
84
|
+
'#f1e2cc'
|
|
85
|
+
],
|
|
86
|
+
// Diverging (for data with midpoint like 0)
|
|
87
|
+
'diverging-prgn': [
|
|
88
|
+
'#40004b',
|
|
89
|
+
'#762a83',
|
|
90
|
+
'#9970ab',
|
|
91
|
+
'#c2a5cf',
|
|
92
|
+
'#e7d4e8',
|
|
93
|
+
'#f7f7f7',
|
|
94
|
+
'#d9f0d3',
|
|
95
|
+
'#a6dba0',
|
|
96
|
+
'#5aae61',
|
|
97
|
+
'#1b7837',
|
|
98
|
+
'#00441b'
|
|
99
|
+
],
|
|
100
|
+
'diverging-spectral': [
|
|
101
|
+
'#9e0142',
|
|
102
|
+
'#d53e4f',
|
|
103
|
+
'#f46d43',
|
|
104
|
+
'#fdae61',
|
|
105
|
+
'#fee08b',
|
|
106
|
+
'#ffffbf',
|
|
107
|
+
'#e6f598',
|
|
108
|
+
'#abdda4',
|
|
109
|
+
'#66c2a5',
|
|
110
|
+
'#3288bd',
|
|
111
|
+
'#5e4fa2'
|
|
112
|
+
],
|
|
113
|
+
'diverging-piyg': [
|
|
114
|
+
'#8e0152',
|
|
115
|
+
'#c51b7d',
|
|
116
|
+
'#de77ae',
|
|
117
|
+
'#f1b6da',
|
|
118
|
+
'#fde0ef',
|
|
119
|
+
'#f7f7f7',
|
|
120
|
+
'#e6f5d0',
|
|
121
|
+
'#b8e186',
|
|
122
|
+
'#7fbc41',
|
|
123
|
+
'#4d9221',
|
|
124
|
+
'#276419'
|
|
125
|
+
],
|
|
126
|
+
'diverging-rdylgn': [
|
|
127
|
+
'#a50026',
|
|
128
|
+
'#d73027',
|
|
129
|
+
'#f46d43',
|
|
130
|
+
'#fdae61',
|
|
131
|
+
'#fee08b',
|
|
132
|
+
'#ffffbf',
|
|
133
|
+
'#d9ef8b',
|
|
134
|
+
'#a6d96a',
|
|
135
|
+
'#66bd63',
|
|
136
|
+
'#1a9850',
|
|
137
|
+
'#006837'
|
|
138
|
+
],
|
|
139
|
+
'diverging-brbg': [
|
|
140
|
+
'#543005',
|
|
141
|
+
'#8c510a',
|
|
142
|
+
'#bf812d',
|
|
143
|
+
'#dfc27d',
|
|
144
|
+
'#f6e8c3',
|
|
145
|
+
'#f5f5f5',
|
|
146
|
+
'#c7eae5',
|
|
147
|
+
'#80cdc1',
|
|
148
|
+
'#35978f',
|
|
149
|
+
'#01665e',
|
|
150
|
+
'#003c30'
|
|
151
|
+
],
|
|
152
|
+
|
|
153
|
+
// Sequential (for continuous or ordered data)
|
|
154
|
+
'sequential-blues': [
|
|
155
|
+
'#f7fbff',
|
|
156
|
+
'#deebf7',
|
|
157
|
+
'#c6dbef',
|
|
158
|
+
'#9ecae1',
|
|
159
|
+
'#6baed6',
|
|
160
|
+
'#4292c6',
|
|
161
|
+
'#2171b5',
|
|
162
|
+
'#08519c',
|
|
163
|
+
'#08306b'
|
|
164
|
+
],
|
|
165
|
+
'sequential-greens': [
|
|
166
|
+
'#f7fcf5',
|
|
167
|
+
'#e5f5e0',
|
|
168
|
+
'#c7e9c0',
|
|
169
|
+
'#a1d99b',
|
|
170
|
+
'#74c476',
|
|
171
|
+
'#41ab5d',
|
|
172
|
+
'#238b45',
|
|
173
|
+
'#006d2c',
|
|
174
|
+
'#00441b'
|
|
175
|
+
],
|
|
176
|
+
'sequential-oranges': [
|
|
177
|
+
'#fff5eb',
|
|
178
|
+
'#fee6ce',
|
|
179
|
+
'#fdd0a2',
|
|
180
|
+
'#fdae6b',
|
|
181
|
+
'#fd8d3c',
|
|
182
|
+
'#f16913',
|
|
183
|
+
'#d94801',
|
|
184
|
+
'#a63603',
|
|
185
|
+
'#7f2704'
|
|
186
|
+
],
|
|
187
|
+
'sequential-purples': [
|
|
188
|
+
'#fcfbfd',
|
|
189
|
+
'#efedf5',
|
|
190
|
+
'#dadaeb',
|
|
191
|
+
'#bcbddc',
|
|
192
|
+
'#9e9ac8',
|
|
193
|
+
'#807dba',
|
|
194
|
+
'#6a51a3',
|
|
195
|
+
'#54278f',
|
|
196
|
+
'#3f007d'
|
|
197
|
+
],
|
|
198
|
+
'sequential-reds': [
|
|
199
|
+
'#fff5f0',
|
|
200
|
+
'#fee0d2',
|
|
201
|
+
'#fcbba1',
|
|
202
|
+
'#fc9272',
|
|
203
|
+
'#fb6a4a',
|
|
204
|
+
'#ef3b2c',
|
|
205
|
+
'#cb181d',
|
|
206
|
+
'#a50f15',
|
|
207
|
+
'#67000d'
|
|
208
|
+
],
|
|
209
|
+
'sequential-ylgnbu': [
|
|
210
|
+
'#ffffd9',
|
|
211
|
+
'#edf8b1',
|
|
212
|
+
'#c7e9b4',
|
|
213
|
+
'#7fcdbb',
|
|
214
|
+
'#41b6c4',
|
|
215
|
+
'#1d91c0',
|
|
216
|
+
'#225ea8',
|
|
217
|
+
'#253494',
|
|
218
|
+
'#081d58'
|
|
219
|
+
]
|
|
220
|
+
};
|
|
221
|
+
|
|
222
|
+
const otherBackgroundColor = 'rgba(212, 212, 212, 0.5)';
|
|
33
223
|
|
|
34
224
|
/**
|
|
35
225
|
* Formats a duration in seconds to a human-readable string showing the two largest units.
|
|
@@ -126,6 +316,18 @@ export class TembaChart extends RapidElement {
|
|
|
126
316
|
@state()
|
|
127
317
|
showConfig: boolean = false;
|
|
128
318
|
|
|
319
|
+
@property({ type: String })
|
|
320
|
+
palette: keyof typeof COLOR_PALETTES;
|
|
321
|
+
|
|
322
|
+
@property({ type: Number })
|
|
323
|
+
opacity: number = 1;
|
|
324
|
+
|
|
325
|
+
@property({ type: Number })
|
|
326
|
+
seriesBorderRadius: number = 2;
|
|
327
|
+
|
|
328
|
+
@property({ type: Number })
|
|
329
|
+
seriesBorderWidth: number = 1;
|
|
330
|
+
|
|
129
331
|
chart: Chart;
|
|
130
332
|
shadowRootDiv: HTMLDivElement;
|
|
131
333
|
canvas: HTMLCanvasElement;
|
|
@@ -214,19 +416,82 @@ export class TembaChart extends RapidElement {
|
|
|
214
416
|
this.data = response.json.data;
|
|
215
417
|
});
|
|
216
418
|
}
|
|
419
|
+
|
|
420
|
+
if (changes.has('chartType')) {
|
|
421
|
+
if (this.chartType === 'line') {
|
|
422
|
+
this.seriesBorderWidth = Math.max(1, this.seriesBorderWidth);
|
|
423
|
+
}
|
|
424
|
+
}
|
|
217
425
|
}
|
|
218
426
|
|
|
427
|
+
/**
|
|
428
|
+
* Returns a tuple: [backgroundColors[], borderColors[]].
|
|
429
|
+
* Border colors are darkened versions of the base palette (before transparency).
|
|
430
|
+
* Background colors have transparency applied.
|
|
431
|
+
*/
|
|
432
|
+
get colors(): [string[], string[]] {
|
|
433
|
+
const baseColors =
|
|
434
|
+
COLOR_PALETTES[this.palette] || COLOR_PALETTES[DEFAULT_PALETTE];
|
|
435
|
+
// Clamp transparency between 0 and 1
|
|
436
|
+
const alpha = Math.max(0, Math.min(1, this.opacity));
|
|
437
|
+
// Borders: darken base color, no transparency
|
|
438
|
+
const borderColors = baseColors.map((color) => darkenColor(color, 0.25));
|
|
439
|
+
// Backgrounds: apply transparency to base color
|
|
440
|
+
const backgroundColors = baseColors.map((color) => {
|
|
441
|
+
// If already rgba, just replace the alpha
|
|
442
|
+
if (color.startsWith('rgba')) {
|
|
443
|
+
return color.replace(
|
|
444
|
+
/rgba\(([^,]+),([^,]+),([^,]+),([^)]+)\)/,
|
|
445
|
+
(_m, r, g, b) => {
|
|
446
|
+
return `rgba(${r},${g},${b},${alpha})`;
|
|
447
|
+
}
|
|
448
|
+
);
|
|
449
|
+
}
|
|
450
|
+
// If already rgb, convert to rgba
|
|
451
|
+
if (color.startsWith('rgb(')) {
|
|
452
|
+
return color.replace(
|
|
453
|
+
/rgb\(([^,]+),([^,]+),([^,]+)\)/,
|
|
454
|
+
(_m, r, g, b) => {
|
|
455
|
+
return `rgba(${r},${g},${b},${alpha})`;
|
|
456
|
+
}
|
|
457
|
+
);
|
|
458
|
+
}
|
|
459
|
+
// If hex, convert to rgba
|
|
460
|
+
if (color.startsWith('#')) {
|
|
461
|
+
let hex = color.replace('#', '');
|
|
462
|
+
if (hex.length === 3) {
|
|
463
|
+
hex = hex
|
|
464
|
+
.split('')
|
|
465
|
+
.map((c) => c + c)
|
|
466
|
+
.join('');
|
|
467
|
+
}
|
|
468
|
+
const num = parseInt(hex, 16);
|
|
469
|
+
const r = (num >> 16) & 255;
|
|
470
|
+
const g = (num >> 8) & 255;
|
|
471
|
+
const b = num & 255;
|
|
472
|
+
return `rgba(${r},${g},${b},${alpha})`;
|
|
473
|
+
}
|
|
474
|
+
// fallback
|
|
475
|
+
return color;
|
|
476
|
+
});
|
|
477
|
+
return [backgroundColors, borderColors];
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
/**
|
|
481
|
+
* Utility to darken an rgba or hex color by a given factor (0-1).
|
|
482
|
+
*/
|
|
483
|
+
|
|
219
484
|
private calculateSplits() {
|
|
220
485
|
if (this.data) {
|
|
221
486
|
const datasets = [];
|
|
222
|
-
// keep a running list of values that is the sum at each index
|
|
223
487
|
const sums = [];
|
|
488
|
+
// Get color arrays
|
|
489
|
+
const [backgroundColors, borderColors] = this.colors;
|
|
224
490
|
for (const dataset of this.data.datasets) {
|
|
225
491
|
if (
|
|
226
492
|
!this.showAll &&
|
|
227
493
|
this.splits.find((s) => s === dataset.label) === undefined
|
|
228
494
|
) {
|
|
229
|
-
// update our sums
|
|
230
495
|
for (let i = 0; i < dataset.data.length; i++) {
|
|
231
496
|
if (sums[i] === undefined) {
|
|
232
497
|
sums[i] = dataset.data[i];
|
|
@@ -235,26 +500,29 @@ export class TembaChart extends RapidElement {
|
|
|
235
500
|
}
|
|
236
501
|
}
|
|
237
502
|
} else {
|
|
503
|
+
const colorIdx =
|
|
504
|
+
(datasets.length + this.colorIndex) % backgroundColors.length;
|
|
505
|
+
const bgColor = backgroundColors[colorIdx];
|
|
506
|
+
const borderColor = borderColors[colorIdx];
|
|
238
507
|
datasets.push({
|
|
239
508
|
...dataset,
|
|
240
|
-
backgroundColor:
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
(datasets.length + this.colorIndex) % colorsBorder.length
|
|
245
|
-
],
|
|
246
|
-
borderWidth: 1
|
|
509
|
+
backgroundColor: bgColor,
|
|
510
|
+
borderColor,
|
|
511
|
+
borderWidth: this.seriesBorderWidth,
|
|
512
|
+
borderRadius: this.seriesBorderRadius
|
|
247
513
|
});
|
|
248
514
|
}
|
|
249
515
|
}
|
|
250
516
|
|
|
251
517
|
if (datasets.length === 0) {
|
|
518
|
+
const idx = this.colorIndex % backgroundColors.length;
|
|
252
519
|
datasets.push({
|
|
253
520
|
label: this.single ? this.dataname : `All ${this.dataname}`,
|
|
254
521
|
data: sums,
|
|
255
|
-
backgroundColor:
|
|
256
|
-
borderColor:
|
|
257
|
-
borderWidth:
|
|
522
|
+
backgroundColor: backgroundColors[idx],
|
|
523
|
+
borderColor: borderColors[idx],
|
|
524
|
+
borderWidth: this.seriesBorderWidth,
|
|
525
|
+
borderRadius: this.seriesBorderRadius
|
|
258
526
|
});
|
|
259
527
|
} else {
|
|
260
528
|
if (!this.hideOther && !this.showAll) {
|
|
@@ -262,8 +530,9 @@ export class TembaChart extends RapidElement {
|
|
|
262
530
|
label: 'Other',
|
|
263
531
|
data: sums,
|
|
264
532
|
backgroundColor: otherBackgroundColor,
|
|
265
|
-
borderColor:
|
|
266
|
-
borderWidth: 1
|
|
533
|
+
borderColor: darkenColor(otherBackgroundColor, 0.05),
|
|
534
|
+
borderWidth: 1,
|
|
535
|
+
borderRadius: this.seriesBorderRadius
|
|
267
536
|
});
|
|
268
537
|
}
|
|
269
538
|
}
|
|
@@ -318,7 +587,8 @@ export class TembaChart extends RapidElement {
|
|
|
318
587
|
return formatDurationFromSeconds(value);
|
|
319
588
|
}
|
|
320
589
|
}
|
|
321
|
-
})
|
|
590
|
+
}),
|
|
591
|
+
grid: { color: 'rgba(0,0,0,0.04)' }
|
|
322
592
|
},
|
|
323
593
|
x: {
|
|
324
594
|
type: 'time',
|
package/src/store/Store.ts
CHANGED
|
@@ -561,6 +561,7 @@ export class Store extends RapidElement {
|
|
|
561
561
|
const orginalUser = last[parts[parts.length - 1]];
|
|
562
562
|
orginalUser.avatar = user.avatar;
|
|
563
563
|
orginalUser.name = getFullName(user);
|
|
564
|
+
orginalUser.uuid = user.uuid;
|
|
564
565
|
last[parts[parts.length - 1]].avatar = user.avatar;
|
|
565
566
|
}
|
|
566
567
|
});
|
package/src/utils/index.ts
CHANGED
|
@@ -802,6 +802,46 @@ export const hslToHex = (h, s, l) => {
|
|
|
802
802
|
return `#${f(0)}${f(8)}${f(4)}`;
|
|
803
803
|
};
|
|
804
804
|
|
|
805
|
+
export const darkenColor = (color: string, factor: number): string => {
|
|
806
|
+
// If rgba or rgb
|
|
807
|
+
const rgbaMatch = color.match(
|
|
808
|
+
/rgba?\((\d+),\s*(\d+),\s*(\d+)(?:,\s*([0-9.]+))?\)/
|
|
809
|
+
);
|
|
810
|
+
if (rgbaMatch) {
|
|
811
|
+
// eslint-disable-next-line prefer-const
|
|
812
|
+
let [r, g, b, a] = rgbaMatch
|
|
813
|
+
.slice(1)
|
|
814
|
+
.map((v, i) => (i < 3 ? parseInt(v) : parseFloat(v)));
|
|
815
|
+
r = Math.max(0, Math.floor(r * (1 - factor)));
|
|
816
|
+
g = Math.max(0, Math.floor(g * (1 - factor)));
|
|
817
|
+
b = Math.max(0, Math.floor(b * (1 - factor)));
|
|
818
|
+
if (rgbaMatch[4] !== undefined) {
|
|
819
|
+
return `rgba(${r},${g},${b},${a})`;
|
|
820
|
+
}
|
|
821
|
+
return `rgb(${r},${g},${b})`;
|
|
822
|
+
}
|
|
823
|
+
// If hex
|
|
824
|
+
if (color.startsWith('#')) {
|
|
825
|
+
let hex = color.replace('#', '');
|
|
826
|
+
if (hex.length === 3) {
|
|
827
|
+
hex = hex
|
|
828
|
+
.split('')
|
|
829
|
+
.map((c) => c + c)
|
|
830
|
+
.join('');
|
|
831
|
+
}
|
|
832
|
+
const num = parseInt(hex, 16);
|
|
833
|
+
let r = (num >> 16) & 255;
|
|
834
|
+
let g = (num >> 8) & 255;
|
|
835
|
+
let b = num & 255;
|
|
836
|
+
r = Math.max(0, Math.floor(r * (1 - factor)));
|
|
837
|
+
g = Math.max(0, Math.floor(g * (1 - factor)));
|
|
838
|
+
b = Math.max(0, Math.floor(b * (1 - factor)));
|
|
839
|
+
return `rgb(${r},${g},${b})`;
|
|
840
|
+
}
|
|
841
|
+
// fallback
|
|
842
|
+
return color;
|
|
843
|
+
};
|
|
844
|
+
|
|
805
845
|
export const renderAvatar = (input: {
|
|
806
846
|
name?: string;
|
|
807
847
|
user?: User;
|