@record-evolution/widget-gauge 1.6.10 → 1.7.1

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,32 +1,51 @@
1
1
  import { html, css, LitElement, PropertyValueMap } from 'lit'
2
- import { property, state } from 'lit/decorators.js'
3
- // import * as echarts from 'echarts'
4
- import type { EChartsOption, GaugeSeriesOption } from 'echarts'
2
+ import { customElement, property, state } from 'lit/decorators.js'
5
3
  import { GaugeChartConfiguration } from './definition-schema.js'
6
4
 
7
- // echarts.use([GaugeChart, CanvasRenderer]);
5
+ import * as echarts from 'echarts/core'
6
+ import { TooltipComponent } from 'echarts/components'
7
+ import { GaugeChart, GaugeSeriesOption } from 'echarts/charts'
8
+ import { CanvasRenderer, SVGRenderer } from 'echarts/renderers'
9
+ import { EChartsOption, SeriesOption } from 'echarts'
10
+
11
+ echarts.use([TooltipComponent, GaugeChart, CanvasRenderer])
8
12
 
9
13
  type Dataseries = Exclude<GaugeChartConfiguration['dataseries'], undefined>[number]
10
14
  type Data = Exclude<Dataseries['data'], undefined>[number]
11
15
 
16
+ @customElement('widget-gauge-versionplaceholder')
12
17
  export class WidgetGauge extends LitElement {
13
18
  @property({ type: Object })
14
19
  inputData?: GaugeChartConfiguration
15
20
 
21
+ @property({ type: Object })
22
+ themeObject?: any
23
+
24
+ @property({ type: String })
25
+ themeName?: string
26
+
16
27
  @state()
17
28
  private dataSets: Dataseries[] = []
18
29
 
19
30
  @state()
20
31
  private canvasList: any = {}
21
32
 
33
+ @state()
34
+ private themeBgColor?: string
35
+
36
+ @state()
37
+ private themeColor?: string
38
+
22
39
  private resizeObserver: ResizeObserver
40
+
23
41
  boxes?: HTMLDivElement[]
24
42
  origWidth: number = 0
25
43
  origHeight: number = 0
26
- template: any
44
+ template: EChartsOption
27
45
  modifier: number = 1
28
46
  version: string = 'versionplaceholder'
29
47
  gaugeContainer: HTMLDivElement | null | undefined
48
+
30
49
  constructor() {
31
50
  super()
32
51
  this.resizeObserver = new ResizeObserver(this.adjustSizes.bind(this))
@@ -46,7 +65,7 @@ export class WidgetGauge extends LitElement {
46
65
  endAngle: 0,
47
66
  min: 33,
48
67
  max: 99,
49
- radius: '121%',
68
+ radius: '140%',
50
69
  center: ['50%', '90%'],
51
70
  progress: {
52
71
  show: true,
@@ -70,22 +89,26 @@ export class WidgetGauge extends LitElement {
70
89
  color: 'inherit'
71
90
  },
72
91
  title: {
92
+ text: 'Gauge A',
73
93
  offsetCenter: [0, '-35%'],
74
- fontSize: 20
94
+ fontSize: 20,
95
+ show: true
75
96
  },
76
97
  data: [
77
98
  {
78
- value: 70
99
+ value: 70,
100
+ name: 'Value Name'
79
101
  }
80
102
  ]
81
- } as GaugeSeriesOption,
103
+ } as SeriesOption,
82
104
  {
83
105
  type: 'gauge',
106
+ name: 'Gauge B',
84
107
  startAngle: 180,
85
108
  endAngle: 0,
86
109
  min: 33,
87
110
  max: 99,
88
- radius: '125%',
111
+ radius: '145%',
89
112
  center: ['50%', '90%'],
90
113
  axisLine: {
91
114
  lineStyle: {
@@ -106,13 +129,19 @@ export class WidgetGauge extends LitElement {
106
129
  color: 'auto'
107
130
  }
108
131
  },
132
+ title: {
133
+ text: 'Gauge B',
134
+ offsetCenter: [0, '-35%'],
135
+ fontSize: 20,
136
+ show: true
137
+ },
109
138
  axisLabel: {
110
139
  distance: -20,
111
140
  color: '#666',
112
141
  rotate: 'tangential',
113
142
  fontSize: 12
114
143
  }
115
- } as GaugeSeriesOption
144
+ } as SeriesOption
116
145
  ]
117
146
  }
118
147
  }
@@ -124,29 +153,41 @@ export class WidgetGauge extends LitElement {
124
153
  }
125
154
  }
126
155
 
127
- update(changedProperties: Map<string, any>) {
128
- changedProperties.forEach((oldValue, propName) => {
129
- if (propName === 'inputData' && this.gaugeContainer) {
130
- this.transformData()
131
- this.adjustSizes()
132
- }
133
- })
156
+ update(changedProperties: PropertyValueMap<any> | Map<PropertyKey, unknown>): void {
157
+ if (changedProperties.has('inputData') && this.gaugeContainer) {
158
+ this.transformData()
159
+ }
160
+
161
+ if (changedProperties.has('themeObject')) {
162
+ this.registerTheme(this.themeName, this.themeObject)
163
+ }
134
164
 
165
+ if (changedProperties.has('themeName')) {
166
+ this.registerTheme(this.themeName, this.themeObject)
167
+ this.deleteCharts()
168
+ this.setupCharts()
169
+ }
135
170
  super.update(changedProperties)
136
171
  }
137
172
 
138
173
  protected firstUpdated(_changedProperties: PropertyValueMap<any> | Map<PropertyKey, unknown>): void {
139
174
  this.resizeObserver.observe(this.shadowRoot?.querySelector('.wrapper') as HTMLDivElement)
140
175
  this.gaugeContainer = this.shadowRoot?.querySelector('.gauge-container')
141
- this.sizingSetup()
142
176
  this.transformData()
143
- this.adjustSizes()
177
+ }
178
+
179
+ registerTheme(themeName?: string, themeObject?: any) {
180
+ if (!themeObject || !themeName) return
181
+
182
+ echarts.registerTheme(themeName, this.themeObject)
144
183
  }
145
184
 
146
185
  sizingSetup() {
147
186
  if (this.origWidth !== 0 && this.origHeight !== 0) return
148
187
 
149
- this.boxes = Array.from(this?.shadowRoot?.querySelectorAll('.chart') as NodeListOf<HTMLDivElement>)
188
+ this.boxes =
189
+ Array.from(this?.shadowRoot?.querySelectorAll('.chart-wrapper') as NodeListOf<HTMLDivElement>) ??
190
+ []
150
191
  if (!this.boxes.length) return
151
192
  this.origWidth =
152
193
  this.boxes?.map((b) => b.getBoundingClientRect().width).reduce((p, c) => (c > p ? c : p), 0) ?? 0
@@ -161,26 +202,26 @@ export class WidgetGauge extends LitElement {
161
202
  const userHeight = this.gaugeContainer.getBoundingClientRect().height
162
203
  const count = this.dataSets.length
163
204
 
164
- const width = this.origWidth
165
- const height = this.origHeight
166
- if (!userHeight || !userWidth || !width || !height) return
205
+ const chartW = this.origWidth
206
+ const chartH = this.origHeight
207
+ if (!userHeight || !userWidth || !chartW || !chartH) return
167
208
  const fits = []
168
209
  for (let c = 1; c <= count; c++) {
169
210
  const r = Math.ceil(count / c)
170
- const uwgap = userWidth - 12 * (c - 1)
171
- const uhgap = userHeight - 12 * (r - 1)
172
- const m = uwgap / width / c
173
- const size = m * m * width * height * count
174
- if (r * m * height <= uhgap) fits.push({ c, r, m, size, uwgap, uhgap })
211
+ const netWidth = userWidth - 24 * (c - 1) // subtract the gaps between the charts
212
+ const netHeight = userHeight - 24 * (r - 1)
213
+ const m = netWidth / chartW / c // modifying factor to make it fit
214
+ const size = m * m * chartW * chartH * count // screen space used by charts overall
215
+ if (r * m * chartH <= netHeight) fits.push({ c, r, m, size, uwgap: netWidth, uhgap: netHeight })
175
216
  }
176
217
 
177
218
  for (let r = 1; r <= count; r++) {
178
219
  const c = Math.ceil(count / r)
179
- const uwgap = userWidth - 12 * (c - 1)
180
- const uhgap = userHeight - 12 * (r - 1)
181
- const m = uhgap / height / r
182
- const size = m * m * width * height * count
183
- if (c * m * width <= uwgap) fits.push({ c, r, m, size, uwgap, uhgap })
220
+ const netWidth = userWidth - 24 * (c - 1)
221
+ const netHeight = userHeight - 24 * (r - 1)
222
+ const m = netHeight / chartH / r
223
+ const size = m * m * chartW * chartH * count
224
+ if (c * m * chartW <= netWidth) fits.push({ c, r, m, size, uwgap: netWidth, uhgap: netHeight })
184
225
  }
185
226
 
186
227
  const maxSize = fits.reduce((p, c) => (c.size < p ? p : c.size), 0)
@@ -202,39 +243,45 @@ export class WidgetGauge extends LitElement {
202
243
  this.boxes = Array.from(this?.shadowRoot?.querySelectorAll('.chart') as NodeListOf<HTMLDivElement>)
203
244
 
204
245
  this.boxes?.forEach((box) =>
205
- box.setAttribute('style', `width:${modifier * width}px; height:${modifier * height}px`)
246
+ box.setAttribute('style', `width:${modifier * chartW}px; height:${modifier * (chartH - 25)}px`)
206
247
  )
207
248
 
208
249
  this.modifier = modifier
209
250
 
210
251
  for (const canvas in this.canvasList) {
211
- this.canvasList[canvas].resize()
252
+ this.canvasList[canvas].echart.resize()
212
253
  }
213
254
  this.applyData()
214
255
  }
215
256
 
216
257
  async transformData() {
217
- if (!this?.inputData) return
258
+ // console.log('Transforming data', this.inputData?.dataseries)
218
259
  this.dataSets = []
219
- this.inputData.dataseries?.forEach((ds) => {
220
- // pivot data
221
- const distincts = [...new Set(ds?.data?.map((d: Data) => d.pivot))]
222
-
223
- distincts.forEach((piv) => {
224
- const prefix = piv ? `${piv} - ` : ''
225
- const pds: any = {
226
- label: prefix + `${ds.label ?? ''}`,
227
- unit: ds.unit,
228
- advanced: ds.advanced,
229
- valueColor: ds.valueColor,
230
- sections: ds.sections,
231
- data: distincts.length === 1 ? ds.data : ds?.data?.filter((d) => d.pivot === piv)
232
- }
233
- this.dataSets.push(pds)
260
+ if (!this?.inputData) return
261
+ this.inputData.dataseries
262
+ ?.sort((a, b) => ((a.label ?? '') > (b.label ?? '') ? 1 : -1))
263
+ .forEach((ds) => {
264
+ // pivot data
265
+ const distincts = [...new Set(ds?.data?.map((d: Data) => d.pivot))]
266
+
267
+ distincts.forEach((piv) => {
268
+ const prefix = piv ?? ''
269
+ const label = ds.label ?? ''
270
+ const pds: any = {
271
+ label: prefix + (!!prefix && !!label ? ' - ' : '') + label,
272
+ unit: ds.unit,
273
+ precision: ds.precision,
274
+ advanced: ds.advanced,
275
+ valueColor: ds.valueColor,
276
+ sections: ds.sections,
277
+ data: distincts.length === 1 ? ds.data : ds?.data?.filter((d) => d.pivot === piv)
278
+ }
279
+ this.dataSets.push(pds)
280
+ })
234
281
  })
235
- })
236
282
 
237
283
  this.setupCharts()
284
+ this.adjustSizes()
238
285
  }
239
286
 
240
287
  applyData() {
@@ -249,6 +296,7 @@ export class WidgetGauge extends LitElement {
249
296
  for (const ds of this.dataSets) {
250
297
  // compute derivative values
251
298
  // filter latest values and calculate average
299
+ ds.label ??= ''
252
300
  ds.advanced ??= {}
253
301
  if (typeof ds.advanced?.averageLatest !== 'number' || isNaN(ds.advanced?.averageLatest))
254
302
  ds.advanced.averageLatest = 1
@@ -265,13 +313,10 @@ export class WidgetGauge extends LitElement {
265
313
  ds.ranges = ds.sections?.sectionLimits?.map((v, i, a) => v - (a?.[i - 1] ?? 0)).slice(1) ?? []
266
314
 
267
315
  // const option = this.canvasList[ds.label].getOption()
268
- const option = structuredClone(this.template)
269
- const ga = option.series[0],
270
- ga2 = option.series[1]
271
-
272
- // Title
273
- option.title.text = ds.label
274
- option.title.textStyle.fontSize = 32 * modifier
316
+ const option = window.structuredClone(this.template)
317
+ const seriesArr = option.series as GaugeSeriesOption[]
318
+ const ga: any = seriesArr?.[0],
319
+ ga2: any = seriesArr?.[1]
275
320
 
276
321
  // Needle
277
322
  // Check age of data Latency
@@ -283,11 +328,12 @@ export class WidgetGauge extends LitElement {
283
328
 
284
329
  ga.data[0].value = ds.needleValue
285
330
  ga.data[0].name = ds.unit
286
- ga.title.fontSize = 32 * modifier
287
- ga.title.color = ds.valueColor ?? 'black'
288
- ga.detail.color = ds.valueColor ?? 'black'
289
- ga.detail.fontSize = 60 * modifier
290
- ga.detail.formatter = (val: number) => (isNaN(val) ? '-' : val.toFixed(0))
331
+ ga.title.fontSize = 25 * modifier
332
+ ga.title.color = ds.valueColor ?? this.themeColor
333
+ ga.detail.color = ds.valueColor ?? this.themeColor
334
+ ga.detail.fontSize = 40 * modifier
335
+ ga.detail.formatter = (val: number) =>
336
+ isNaN(val) ? '-' : val.toFixed(Math.floor(ds.precision ?? 0))
291
337
  // ga.anchor.itemStyle.color = ds.valueColor
292
338
  // ga.pointer.itemStyle.color = ds.valueColor
293
339
 
@@ -313,21 +359,32 @@ export class WidgetGauge extends LitElement {
313
359
  ga2.splitLine.distance = -16 * modifier
314
360
 
315
361
  // Progress
316
- let progressColor =
317
- ds.sections?.backgroundColors?.[ds.sections?.backgroundColors.length - 1] ?? 'green'
362
+ let progressColor = ds.sections?.backgroundColors?.[ds.sections?.backgroundColors.length - 1]
318
363
  for (const [i, s] of ds.sections?.sectionLimits?.entries() ?? []) {
319
364
  if (s > (ds.needleValue as number)) {
320
365
  progressColor =
321
- ds.sections?.backgroundColors?.[i - 1] ??
322
- ds.sections?.backgroundColors?.[0] ??
323
- 'green'
366
+ ds.sections?.backgroundColors?.[i - 1] ?? ds.sections?.backgroundColors?.[0]
324
367
  break
325
368
  }
326
369
  }
327
370
  ga.progress.itemStyle.color = progressColor
328
- ga.progress.width = 80 * modifier
371
+ ga.progress.width = 60 * modifier
329
372
  // Apply
330
- this.canvasList[ds.label ?? '']?.setOption(option)
373
+ const titleElement = this.canvasList[ds.label]?.title
374
+ titleElement.style.fontSize = String(20 * modifier) + 'px'
375
+ titleElement.style.maxWidth = String(300 * modifier) + 'px'
376
+ titleElement.style.height = String(25 * modifier) + 'px'
377
+ titleElement.textContent = ds.label ?? ''
378
+
379
+ this.canvasList[ds.label]?.echart.setOption(option)
380
+ }
381
+ }
382
+
383
+ deleteCharts() {
384
+ for (const label in this.canvasList) {
385
+ this.canvasList[label].echart.dispose()
386
+ this.canvasList[label].wrapper?.remove()
387
+ delete this.canvasList[label]
331
388
  }
332
389
  }
333
390
 
@@ -336,26 +393,37 @@ export class WidgetGauge extends LitElement {
336
393
  for (const label in this.canvasList) {
337
394
  const ex = this.dataSets.find((ds) => ds.label === label)
338
395
  if (!ex) {
396
+ this.canvasList[label].echart.dispose()
397
+ this.canvasList[label].wrapper?.remove()
339
398
  delete this.canvasList[label]
340
- const containerDiv = this.gaugeContainer?.querySelector(`[name="${label}"]`)
341
- containerDiv?.remove()
342
399
  }
343
400
  }
344
401
 
345
402
  this.dataSets.forEach((ds) => {
346
403
  if (this.canvasList[ds.label ?? '']) return
404
+ const newWrapper = document.createElement('div')
405
+ newWrapper.setAttribute('class', 'chart-wrapper')
406
+ const newTitle = document.createElement('h3')
407
+ newTitle.style.fontSize = '20px'
347
408
  const newCanvas = document.createElement('div')
348
409
  newCanvas.setAttribute('name', ds.label ?? '')
349
410
  newCanvas.setAttribute('class', 'chart')
350
411
  newCanvas.setAttribute(
351
412
  'style',
352
- `min-width: 600px; min-height: 400px; width: 600px; height: 400px;`
413
+ `min-width: 600px; min-height: 250px; width: 600px; height: 250px;`
353
414
  )
354
415
 
355
- this.gaugeContainer!.appendChild(newCanvas)
356
- // @ts-ignore
357
- this.canvasList[ds.label ?? ''] = echarts.init(newCanvas)
358
- this.canvasList[ds.label ?? ''].setOption(structuredClone(this.template))
416
+ newWrapper!.appendChild(newTitle)
417
+ newWrapper!.appendChild(newCanvas)
418
+ this.gaugeContainer!.appendChild(newWrapper)
419
+
420
+ const newChart = echarts.init(newCanvas, this.themeName)
421
+ this.canvasList[ds.label ?? ''] = { echart: newChart, title: newTitle, wrapper: newWrapper }
422
+ // this.canvasList[ds.label ?? ''].setOption(structuredClone(this.template))
423
+ //@ts-ignore
424
+ this.themeBgColor = newChart?._theme?.backgroundColor
425
+ //@ts-ignore
426
+ this.themeColor = newChart?._theme?.gauge?.title?.color
359
427
  })
360
428
  this.sizingSetup()
361
429
  }
@@ -381,6 +449,14 @@ export class WidgetGauge extends LitElement {
381
449
  width: 100%;
382
450
  padding: 16px;
383
451
  box-sizing: border-box;
452
+ color: var(--re-text-color, #000);
453
+ gap: 12px;
454
+ }
455
+
456
+ .chart-wrapper {
457
+ display: flex;
458
+ flex-direction: column;
459
+ align-items: center;
384
460
  }
385
461
  .gauge-container {
386
462
  display: flex;
@@ -390,13 +466,12 @@ export class WidgetGauge extends LitElement {
390
466
  flex-wrap: wrap;
391
467
  overflow: hidden;
392
468
  position: relative;
393
- gap: 12px;
469
+ gap: 24px;
394
470
  }
395
471
 
396
472
  header {
397
473
  display: flex;
398
474
  flex-direction: column;
399
- margin: 0 0 16px 0;
400
475
  }
401
476
  h3 {
402
477
  margin: 0;
@@ -404,7 +479,6 @@ export class WidgetGauge extends LitElement {
404
479
  overflow: hidden;
405
480
  text-overflow: ellipsis;
406
481
  white-space: nowrap;
407
- color: var(--re-text-color, #000) !important;
408
482
  }
409
483
  p {
410
484
  margin: 10px 0 0 0;
@@ -414,12 +488,11 @@ export class WidgetGauge extends LitElement {
414
488
  overflow: hidden;
415
489
  text-overflow: ellipsis;
416
490
  white-space: nowrap;
417
- color: var(--re-text-color, #000) !important;
418
491
  }
419
492
 
420
493
  .chart {
421
494
  width: 600px; /* will be overriden by adjustSizes */
422
- height: 400px;
495
+ height: 230px;
423
496
  }
424
497
 
425
498
  .no-data {
@@ -436,7 +509,7 @@ export class WidgetGauge extends LitElement {
436
509
 
437
510
  render() {
438
511
  return html`
439
- <div class="wrapper">
512
+ <div class="wrapper" style="background-color: ${this.themeBgColor}; color: ${this.themeColor}">
440
513
  <header class="paging" ?active=${this.inputData?.title || this.inputData?.subTitle}>
441
514
  <h3 class="paging" ?active=${this.inputData?.title}>${this.inputData?.title}</h3>
442
515
  <p class="paging" ?active=${this.inputData?.subTitle}>${this.inputData?.subTitle}</p>
@@ -447,5 +520,3 @@ export class WidgetGauge extends LitElement {
447
520
  `
448
521
  }
449
522
  }
450
-
451
- window.customElements.define('widget-gauge-versionplaceholder', WidgetGauge)