@record-evolution/widget-linechart 1.6.33 → 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.
@@ -39,7 +39,12 @@ type Theme = {
39
39
  theme_object: any
40
40
  }
41
41
 
42
- type SeriesOptionX = SeriesOption & { minDate?: number; maxDate?: number; drawOrder: number }
42
+ type SeriesOptionX = SeriesOption & {
43
+ minDate?: number
44
+ maxDate?: number
45
+ drawOrder: number
46
+ yAxisIndex?: number
47
+ }
43
48
  @customElement('widget-linechart-versionplaceholder')
44
49
  export class WidgetLinechart extends LitElement {
45
50
  @property({ type: Object })
@@ -143,21 +148,45 @@ export class WidgetLinechart extends LitElement {
143
148
  fontSize: 14
144
149
  }
145
150
  },
146
- yAxis: {
147
- type: 'value',
148
- nameLocation: 'middle',
149
- name: 'Temperature (°C)',
150
- nameGap: 30,
151
- axisLabel: {
152
- fontSize: 14
153
- },
154
- axisLine: {
155
- lineStyle: {
156
- width: undefined
157
- }
151
+ // index 0 = left (primary) axis, index 1 = right (secondary) axis.
152
+ // Must stay function-free: the template is copied via structuredClone.
153
+ yAxis: [
154
+ {
155
+ type: 'value',
156
+ nameLocation: 'middle',
157
+ name: 'Temperature (°C)',
158
+ nameGap: 30,
159
+ position: 'left',
160
+ axisLabel: {
161
+ fontSize: 14
162
+ },
163
+ axisLine: {
164
+ lineStyle: {
165
+ width: undefined
166
+ }
167
+ },
168
+ scale: false
158
169
  },
159
- scale: false
160
- },
170
+ {
171
+ type: 'value',
172
+ position: 'right',
173
+ show: false,
174
+ name: '',
175
+ nameGap: 30,
176
+ axisLabel: {
177
+ fontSize: 14
178
+ },
179
+ axisLine: {
180
+ lineStyle: {
181
+ width: undefined
182
+ }
183
+ },
184
+ splitLine: {
185
+ show: false
186
+ },
187
+ scale: false
188
+ }
189
+ ],
161
190
  series: [
162
191
  {
163
192
  name: 'Highest',
@@ -185,6 +214,10 @@ export class WidgetLinechart extends LitElement {
185
214
  }
186
215
 
187
216
  update(changedProperties: PropertyValueMap<any> | Map<PropertyKey, unknown>): void {
217
+ if (changedProperties.has('inputData')) {
218
+ this.cachedXAxisType = undefined
219
+ this.cachedYAxisType = undefined
220
+ }
188
221
  if (changedProperties.has('inputData') && this.chartContainer) {
189
222
  // const drawingStates = Array.from(this.canvasList).map(([key, chart]) => chart.drawing)
190
223
  // if (drawingStates.every((d) => !d)) {
@@ -314,7 +347,8 @@ export class WidgetLinechart extends LitElement {
314
347
  symbolSize: (d: any[]) => d[2] ?? 0,
315
348
  showSymbol: ds.styling?.pointStyle === 'none' ? false : true,
316
349
  data: data2 ?? [],
317
- drawOrder: ds.advanced?.drawOrder ?? 0
350
+ drawOrder: ds.advanced?.drawOrder ?? 0,
351
+ yAxisIndex: ds.yAxis === 'right' ? 1 : 0
318
352
  }
319
353
  let chartName = ds.advanced?.chartName ?? ''
320
354
  chartName = chartName.replace('#split#', prefix)
@@ -363,17 +397,38 @@ export class WidgetLinechart extends LitElement {
363
397
  }
364
398
  }
365
399
 
400
+ // cached per inputData change — cleared in update(), see xAxisType()/yAxisType()
401
+ private cachedXAxisType?: 'value' | 'category' | 'time'
402
+ private cachedYAxisType?: 'value' | 'category'
403
+
366
404
  xAxisType(): 'value' | 'log' | 'category' | 'time' | undefined {
367
- if (this.inputData?.axis?.timeseries) return 'time'
368
- const onePoint = this.inputData?.dataseries?.[0]?.data?.[0]
369
- if (!isNaN(Number(onePoint?.x))) return 'value'
370
- return 'category'
405
+ if (this.cachedXAxisType) return this.cachedXAxisType
406
+ if (this.inputData?.axis?.timeseries) {
407
+ this.cachedXAxisType = 'time'
408
+ return this.cachedXAxisType
409
+ }
410
+ this.cachedXAxisType = 'value'
411
+ for (const ds of this.inputData?.dataseries ?? []) {
412
+ const point = ds.data?.find((d) => d.x !== undefined && d.x !== null)
413
+ if (point) {
414
+ this.cachedXAxisType = !isNaN(Number(point.x)) ? 'value' : 'category'
415
+ break
416
+ }
417
+ }
418
+ return this.cachedXAxisType
371
419
  }
372
420
 
373
421
  yAxisType(): 'value' | 'log' | 'category' | undefined {
374
- const onePoint = this.inputData?.dataseries?.[0]?.data?.[0]
375
- if (!isNaN(Number(onePoint?.y))) return 'value'
376
- return 'category'
422
+ if (this.cachedYAxisType) return this.cachedYAxisType
423
+ this.cachedYAxisType = 'value'
424
+ for (const ds of this.inputData?.dataseries ?? []) {
425
+ const point = ds.data?.find((d) => d.y !== undefined && d.y !== null)
426
+ if (point) {
427
+ this.cachedYAxisType = !isNaN(Number(point.y)) ? 'value' : 'category'
428
+ break
429
+ }
430
+ }
431
+ return this.cachedYAxisType
377
432
  }
378
433
 
379
434
  calculateAnimationDuration(chart: any, label: string): number {
@@ -454,10 +509,13 @@ export class WidgetLinechart extends LitElement {
454
509
  yAxisLabel: this.inputData?.axis?.yAxisLabel,
455
510
  xAxisZoom: this.inputData?.axis?.xAxisZoom,
456
511
  yAxisScaling: this.inputData?.axis?.yAxisScaling,
512
+ yAxisLabelRight: this.inputData?.axis?.yAxisLabelRight,
513
+ yAxisScalingRight: this.inputData?.axis?.yAxisScalingRight,
457
514
  xAxisType: this.xAxisType(),
458
515
  yAxisType: this.yAxisType(),
459
516
  seriesCount: chart.series.length,
460
- seriesNames: chart.series.map((s) => s.name).join(',')
517
+ seriesNames: chart.series.map((s) => s.name).join(','),
518
+ seriesAxes: chart.series.map((s) => s.yAxisIndex ?? 0).join(',')
461
519
  })
462
520
  const configChanged = chart.lastConfig !== currentConfig
463
521
  chart.lastConfig = currentConfig
@@ -498,28 +556,66 @@ export class WidgetLinechart extends LitElement {
498
556
  option.dataZoom[0].show = this.inputData?.axis?.xAxisZoom ?? false
499
557
  option.toolbox.show = this.inputData?.axis?.xAxisZoom ?? false
500
558
 
559
+ // Y axes: index 0 = left (primary), index 1 = right (secondary).
560
+ // The template holds an array, but getOption() (merge path) also returns
561
+ // component options normalized to arrays — handle both shapes.
562
+ const yAxes: any[] = Array.isArray(option.yAxis) ? option.yAxis : [option.yAxis ?? {}]
563
+ while (yAxes.length < 2) yAxes.push({})
564
+ option.yAxis = yAxes
565
+
566
+ const rightAxisUsed = chart.series.some((s) => (s.yAxisIndex ?? 0) === 1)
567
+ const leftAxisUsed =
568
+ chart.series.length === 0 || chart.series.some((s) => (s.yAxisIndex ?? 0) === 0)
569
+ const showLeftAxis = showYAxis && leftAxisUsed
570
+ const showRightAxis = showYAxis && rightAxisUsed
571
+
501
572
  const yAxisLabel = this.inputData?.axis?.yAxisLabel ?? ''
502
- const hasYAxisLabel = showYAxis && !!yAxisLabel
503
- option.yAxis.type = this.yAxisType()
504
- option.yAxis.name = yAxisLabel
505
- option.yAxis.scale = this.inputData?.axis?.yAxisScaling ?? false
506
- option.yAxis.show = showYAxis
507
- option.yAxis.nameLocation = 'end'
508
- option.yAxis.nameGap = 10
509
- option.yAxis.nameTextStyle = { align: 'left' }
510
- option.yAxis.axisLine = { show: true }
511
- if (['value', 'log'].includes(option.yAxis.type))
512
- option.yAxis.axisLabel = {
513
- 'font-size': 14,
514
- formatter: (value: number) => Math.round(value * 100) / 100
515
- }
573
+ const yAxisLabelRight = this.inputData?.axis?.yAxisLabelRight ?? ''
574
+ const hasYAxisLabel = showLeftAxis && !!yAxisLabel
575
+ const hasYAxisLabelRight = showRightAxis && !!yAxisLabelRight
576
+
577
+ const yType = this.yAxisType()
578
+ const numericAxisLabel = ['value', 'log'].includes(yType ?? '')
579
+ ? { fontSize: 14, formatter: (value: number) => Math.round(value * 100) / 100 }
580
+ : undefined
581
+
582
+ Object.assign(yAxes[0], {
583
+ type: yType,
584
+ name: yAxisLabel,
585
+ scale: this.inputData?.axis?.yAxisScaling ?? false,
586
+ show: showLeftAxis,
587
+ position: 'left',
588
+ nameLocation: 'end',
589
+ nameGap: 10,
590
+ nameTextStyle: { align: 'left' },
591
+ axisLine: { show: true }
592
+ })
593
+ if (numericAxisLabel) yAxes[0].axisLabel = numericAxisLabel
594
+
595
+ Object.assign(yAxes[1], {
596
+ type: yType,
597
+ name: showRightAxis ? yAxisLabelRight : '',
598
+ scale: this.inputData?.axis?.yAxisScalingRight ?? false,
599
+ show: showRightAxis,
600
+ position: 'right',
601
+ nameLocation: 'end',
602
+ nameGap: 10,
603
+ nameTextStyle: { align: 'right' },
604
+ axisLine: { show: true },
605
+ splitLine: { show: false }
606
+ })
607
+ if (numericAxisLabel) yAxes[1].axisLabel = numericAxisLabel
516
608
 
517
609
  option.series = chart.series
518
610
  option.legend.show = showLegend
519
611
 
520
612
  // Dynamic grid padding based on visible elements
521
- // Add extra top space when Y-axis label is shown at 'end' position
522
- const topPadding = showTitle ? 30 : hasYAxisLabel ? 30 : 0
613
+ // Add extra top space when Y-axis label is shown at 'end' position.
614
+ // The right axis name and the legend both live in the top-right corner,
615
+ // so reserve an extra row when both are visible.
616
+ const topPadding =
617
+ (showTitle || hasYAxisLabel || hasYAxisLabelRight ? 30 : 0) +
618
+ (showLegend && hasYAxisLabelRight ? 25 : 0)
523
619
  option.grid = {
524
620
  ...option.grid,
525
621
  show: showBox,
@@ -528,7 +624,7 @@ export class WidgetLinechart extends LitElement {
528
624
  borderColor: this.themeTitleColor ?? '#ccc',
529
625
  top: topPadding,
530
626
  bottom: showXAxis ? 20 : 0,
531
- left: showYAxis ? 20 : 0,
627
+ left: showLeftAxis ? 20 : 0,
532
628
  right: 0,
533
629
  containLabel: showXAxis || showYAxis
534
630
  }