| 
									
										
										
										
											2021-04-07 07:06:46 +08:00
										 |  |  | import { | 
					
						
							|  |  |  |   DataFrame, | 
					
						
							| 
									
										
										
										
											2021-06-16 12:35:44 +08:00
										 |  |  |   FALLBACK_COLOR, | 
					
						
							|  |  |  |   Field, | 
					
						
							|  |  |  |   FieldColorModeId, | 
					
						
							| 
									
										
										
										
											2021-04-07 07:06:46 +08:00
										 |  |  |   FieldConfig, | 
					
						
							| 
									
										
										
										
											2021-06-16 12:35:44 +08:00
										 |  |  |   FieldType, | 
					
						
							| 
									
										
										
										
											2021-04-07 07:06:46 +08:00
										 |  |  |   formattedValueToString, | 
					
						
							|  |  |  |   getFieldDisplayName, | 
					
						
							| 
									
										
										
										
											2021-05-20 12:38:31 +08:00
										 |  |  |   getValueFormat, | 
					
						
							| 
									
										
										
										
											2021-05-20 14:17:50 +08:00
										 |  |  |   GrafanaTheme2, | 
					
						
							| 
									
										
										
										
											2021-09-01 23:43:57 +08:00
										 |  |  |   getActiveThreshold, | 
					
						
							|  |  |  |   Threshold, | 
					
						
							|  |  |  |   getFieldConfigWithMinMax, | 
					
						
							| 
									
										
										
										
											2021-06-16 12:35:44 +08:00
										 |  |  |   ThresholdsMode, | 
					
						
							| 
									
										
										
										
											2022-06-04 05:22:57 +08:00
										 |  |  |   TimeRange, | 
					
						
							| 
									
										
										
										
											2024-02-17 14:38:13 +08:00
										 |  |  |   cacheFieldDisplayNames, | 
					
						
							| 
									
										
										
										
											2024-03-11 11:11:11 +08:00
										 |  |  |   outerJoinDataFrames, | 
					
						
							| 
									
										
										
										
											2024-09-27 23:02:03 +08:00
										 |  |  |   ValueMapping, | 
					
						
							|  |  |  |   ThresholdsConfig, | 
					
						
							| 
									
										
										
										
											2021-04-07 07:06:46 +08:00
										 |  |  | } from '@grafana/data'; | 
					
						
							| 
									
										
										
										
											2024-03-11 11:11:11 +08:00
										 |  |  | import { maybeSortFrame, NULL_RETAIN } from '@grafana/data/src/transformations/transformers/joinDataFrames'; | 
					
						
							| 
									
										
										
										
											2023-11-02 12:59:55 +08:00
										 |  |  | import { applyNullInsertThreshold } from '@grafana/data/src/transformations/transformers/nulls/nullInsertThreshold'; | 
					
						
							|  |  |  | import { nullToValue } from '@grafana/data/src/transformations/transformers/nulls/nullToValue'; | 
					
						
							| 
									
										
										
										
											2023-01-26 15:03:59 +08:00
										 |  |  | import { | 
					
						
							|  |  |  |   VizLegendOptions, | 
					
						
							|  |  |  |   AxisPlacement, | 
					
						
							|  |  |  |   ScaleDirection, | 
					
						
							|  |  |  |   ScaleOrientation, | 
					
						
							|  |  |  |   VisibilityMode, | 
					
						
							|  |  |  |   TimelineValueAlignment, | 
					
						
							|  |  |  |   HideableFieldConfig, | 
					
						
							| 
									
										
										
										
											2023-05-26 02:38:32 +08:00
										 |  |  |   MappingType, | 
					
						
							| 
									
										
										
										
											2023-01-26 15:03:59 +08:00
										 |  |  | } from '@grafana/schema'; | 
					
						
							| 
									
										
										
										
											2024-04-03 04:32:46 +08:00
										 |  |  | import { FIXED_UNIT, UPlotConfigBuilder, UPlotConfigPrepFn, VizLegendItem } from '@grafana/ui'; | 
					
						
							| 
									
										
										
										
											2025-03-12 21:14:32 +08:00
										 |  |  | import { preparePlotData2, getStackingGroups } from '@grafana/ui/internal'; | 
					
						
							| 
									
										
										
										
											2022-04-22 21:33:13 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | import { getConfig, TimelineCoreOptions } from './timeline'; | 
					
						
							| 
									
										
										
										
											2021-04-07 07:06:46 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-26 15:03:59 +08:00
										 |  |  | /** | 
					
						
							|  |  |  |  * @internal | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | interface UPlotConfigOptions { | 
					
						
							|  |  |  |   frame: DataFrame; | 
					
						
							|  |  |  |   theme: GrafanaTheme2; | 
					
						
							|  |  |  |   mode: TimelineMode; | 
					
						
							|  |  |  |   rowHeight?: number; | 
					
						
							|  |  |  |   colWidth?: number; | 
					
						
							|  |  |  |   showValue: VisibilityMode; | 
					
						
							|  |  |  |   alignValue?: TimelineValueAlignment; | 
					
						
							|  |  |  |   mergeValues?: boolean; | 
					
						
							| 
									
										
										
										
											2023-03-14 17:51:44 +08:00
										 |  |  |   getValueColor: (frameIdx: number, fieldIdx: number, value: unknown) => string; | 
					
						
							| 
									
										
										
										
											2024-02-16 02:54:43 +08:00
										 |  |  |   hoverMulti: boolean; | 
					
						
							| 
									
										
										
										
											2025-01-24 02:21:24 +08:00
										 |  |  |   axisWidth?: number; | 
					
						
							| 
									
										
										
										
											2023-01-26 15:03:59 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /** | 
					
						
							|  |  |  |  * @internal | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | interface PanelFieldConfig extends HideableFieldConfig { | 
					
						
							|  |  |  |   fillOpacity?: number; | 
					
						
							|  |  |  |   lineWidth?: number; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | export enum TimelineMode { | 
					
						
							|  |  |  |   Changes = 'changes', | 
					
						
							|  |  |  |   Samples = 'samples', | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | const defaultConfig: PanelFieldConfig = { | 
					
						
							| 
									
										
										
										
											2021-04-07 07:06:46 +08:00
										 |  |  |   lineWidth: 0, | 
					
						
							|  |  |  |   fillOpacity: 80, | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-26 15:03:59 +08:00
										 |  |  | export const preparePlotConfigBuilder: UPlotConfigPrepFn<UPlotConfigOptions> = ({ | 
					
						
							| 
									
										
										
										
											2021-05-05 16:44:31 +08:00
										 |  |  |   frame, | 
					
						
							|  |  |  |   theme, | 
					
						
							| 
									
										
										
										
											2022-07-23 11:18:27 +08:00
										 |  |  |   timeZones, | 
					
						
							| 
									
										
										
										
											2021-05-05 16:44:31 +08:00
										 |  |  |   getTimeRange, | 
					
						
							|  |  |  |   mode, | 
					
						
							|  |  |  |   rowHeight, | 
					
						
							|  |  |  |   colWidth, | 
					
						
							|  |  |  |   showValue, | 
					
						
							| 
									
										
										
										
											2021-05-14 14:27:03 +08:00
										 |  |  |   alignValue, | 
					
						
							| 
									
										
										
										
											2022-02-03 00:25:49 +08:00
										 |  |  |   mergeValues, | 
					
						
							| 
									
										
										
										
											2022-02-24 16:38:49 +08:00
										 |  |  |   getValueColor, | 
					
						
							| 
									
										
										
										
											2024-02-16 02:54:43 +08:00
										 |  |  |   hoverMulti, | 
					
						
							| 
									
										
										
										
											2021-05-05 16:44:31 +08:00
										 |  |  | }) => { | 
					
						
							| 
									
										
										
										
											2022-07-23 11:18:27 +08:00
										 |  |  |   const builder = new UPlotConfigBuilder(timeZones[0]); | 
					
						
							| 
									
										
										
										
											2021-04-07 07:06:46 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-11-06 01:52:40 +08:00
										 |  |  |   const xScaleKey = 'x'; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-04-07 07:06:46 +08:00
										 |  |  |   const isDiscrete = (field: Field) => { | 
					
						
							|  |  |  |     const mode = field.config?.color?.mode; | 
					
						
							|  |  |  |     return !(mode && field.display && mode.startsWith('continuous-')); | 
					
						
							|  |  |  |   }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-05-26 02:38:32 +08:00
										 |  |  |   const hasMappedNull = (field: Field) => { | 
					
						
							|  |  |  |     return ( | 
					
						
							|  |  |  |       field.config.mappings?.some( | 
					
						
							|  |  |  |         (mapping) => mapping.type === MappingType.SpecialValue && mapping.options.match === 'null' | 
					
						
							|  |  |  |       ) || false | 
					
						
							|  |  |  |     ); | 
					
						
							|  |  |  |   }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-03-14 17:51:44 +08:00
										 |  |  |   const getValueColorFn = (seriesIdx: number, value: unknown) => { | 
					
						
							| 
									
										
										
										
											2021-04-07 07:06:46 +08:00
										 |  |  |     const field = frame.fields[seriesIdx]; | 
					
						
							| 
									
										
										
										
											2021-05-14 03:41:40 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-02-24 16:38:49 +08:00
										 |  |  |     if ( | 
					
						
							|  |  |  |       field.state?.origin?.fieldIndex !== undefined && | 
					
						
							|  |  |  |       field.state?.origin?.frameIndex !== undefined && | 
					
						
							|  |  |  |       getValueColor | 
					
						
							|  |  |  |     ) { | 
					
						
							|  |  |  |       return getValueColor(field.state?.origin?.frameIndex, field.state?.origin?.fieldIndex, value); | 
					
						
							| 
									
										
										
										
											2021-04-07 07:06:46 +08:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2021-05-14 03:41:40 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  |     return FALLBACK_COLOR; | 
					
						
							| 
									
										
										
										
											2021-04-07 07:06:46 +08:00
										 |  |  |   }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   const opts: TimelineCoreOptions = { | 
					
						
							| 
									
										
										
										
											2021-05-05 16:44:31 +08:00
										 |  |  |     mode: mode!, | 
					
						
							| 
									
										
										
										
											2021-04-07 07:06:46 +08:00
										 |  |  |     numSeries: frame.fields.length - 1, | 
					
						
							|  |  |  |     isDiscrete: (seriesIdx) => isDiscrete(frame.fields[seriesIdx]), | 
					
						
							| 
									
										
										
										
											2023-05-26 02:38:32 +08:00
										 |  |  |     hasMappedNull: (seriesIdx) => hasMappedNull(frame.fields[seriesIdx]), | 
					
						
							| 
									
										
										
										
											2022-02-03 00:25:49 +08:00
										 |  |  |     mergeValues, | 
					
						
							| 
									
										
										
										
											2023-01-26 15:03:59 +08:00
										 |  |  |     rowHeight: rowHeight, | 
					
						
							| 
									
										
										
										
											2021-05-05 16:44:31 +08:00
										 |  |  |     colWidth: colWidth, | 
					
						
							|  |  |  |     showValue: showValue!, | 
					
						
							| 
									
										
										
										
											2021-05-14 14:27:03 +08:00
										 |  |  |     alignValue, | 
					
						
							| 
									
										
										
										
											2021-05-14 03:41:40 +08:00
										 |  |  |     theme, | 
					
						
							| 
									
										
										
										
											2021-04-07 07:06:46 +08:00
										 |  |  |     label: (seriesIdx) => getFieldDisplayName(frame.fields[seriesIdx], frame), | 
					
						
							| 
									
										
										
										
											2021-05-14 23:24:40 +08:00
										 |  |  |     getFieldConfig: (seriesIdx) => frame.fields[seriesIdx].config.custom, | 
					
						
							| 
									
										
										
										
											2022-02-24 16:38:49 +08:00
										 |  |  |     getValueColor: getValueColorFn, | 
					
						
							| 
									
										
										
										
											2021-04-07 07:06:46 +08:00
										 |  |  |     getTimeRange, | 
					
						
							|  |  |  |     // hardcoded formatter for state values
 | 
					
						
							|  |  |  |     formatValue: (seriesIdx, value) => formattedValueToString(frame.fields[seriesIdx].display!(value)), | 
					
						
							| 
									
										
										
										
											2024-02-16 02:54:43 +08:00
										 |  |  |     hoverMulti, | 
					
						
							| 
									
										
										
										
											2021-04-07 07:06:46 +08:00
										 |  |  |   }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   const coreConfig = getConfig(opts); | 
					
						
							| 
									
										
										
										
											2024-02-09 06:15:32 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-04-07 07:06:46 +08:00
										 |  |  |   builder.addHook('init', coreConfig.init); | 
					
						
							|  |  |  |   builder.addHook('drawClear', coreConfig.drawClear); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-04-13 23:29:03 +08:00
										 |  |  |   builder.setPrepData((frames) => preparePlotData2(frames[0], getStackingGroups(frames[0]))); | 
					
						
							| 
									
										
										
										
											2021-07-29 09:31:07 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-04-07 07:06:46 +08:00
										 |  |  |   builder.setCursor(coreConfig.cursor); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   builder.addScale({ | 
					
						
							| 
									
										
										
										
											2021-11-06 01:52:40 +08:00
										 |  |  |     scaleKey: xScaleKey, | 
					
						
							| 
									
										
										
										
											2021-04-07 07:06:46 +08:00
										 |  |  |     isTime: true, | 
					
						
							|  |  |  |     orientation: ScaleOrientation.Horizontal, | 
					
						
							|  |  |  |     direction: ScaleDirection.Right, | 
					
						
							|  |  |  |     range: coreConfig.xRange, | 
					
						
							|  |  |  |   }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   builder.addScale({ | 
					
						
							|  |  |  |     scaleKey: FIXED_UNIT, // y
 | 
					
						
							|  |  |  |     isTime: false, | 
					
						
							|  |  |  |     orientation: ScaleOrientation.Vertical, | 
					
						
							|  |  |  |     direction: ScaleDirection.Up, | 
					
						
							|  |  |  |     range: coreConfig.yRange, | 
					
						
							|  |  |  |   }); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-01-24 02:21:24 +08:00
										 |  |  |   const xAxisHidden = frame.fields[0].config.custom.axisPlacement === AxisPlacement.Hidden; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-04-07 07:06:46 +08:00
										 |  |  |   builder.addAxis({ | 
					
						
							| 
									
										
										
										
											2025-01-24 02:21:24 +08:00
										 |  |  |     show: !xAxisHidden, | 
					
						
							| 
									
										
										
										
											2021-11-06 01:52:40 +08:00
										 |  |  |     scaleKey: xScaleKey, | 
					
						
							| 
									
										
										
										
											2021-04-07 07:06:46 +08:00
										 |  |  |     isTime: true, | 
					
						
							|  |  |  |     splits: coreConfig.xSplits!, | 
					
						
							|  |  |  |     placement: AxisPlacement.Bottom, | 
					
						
							| 
									
										
										
										
											2022-07-23 11:18:27 +08:00
										 |  |  |     timeZone: timeZones[0], | 
					
						
							| 
									
										
										
										
											2021-04-07 07:06:46 +08:00
										 |  |  |     theme, | 
					
						
							|  |  |  |   }); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-01-24 02:21:24 +08:00
										 |  |  |   const yCustomConfig = frame.fields[1].config.custom; | 
					
						
							|  |  |  |   const yAxisWidth = yCustomConfig.axisWidth; | 
					
						
							|  |  |  |   const yAxisHidden = yCustomConfig.axisPlacement === AxisPlacement.Hidden; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-04-07 07:06:46 +08:00
										 |  |  |   builder.addAxis({ | 
					
						
							|  |  |  |     scaleKey: FIXED_UNIT, // y
 | 
					
						
							|  |  |  |     isTime: false, | 
					
						
							|  |  |  |     placement: AxisPlacement.Left, | 
					
						
							|  |  |  |     splits: coreConfig.ySplits, | 
					
						
							| 
									
										
										
										
											2025-01-24 02:21:24 +08:00
										 |  |  |     values: yAxisHidden ? (u, splits) => splits.map((v) => null) : coreConfig.yValues, | 
					
						
							| 
									
										
										
										
											2021-08-28 01:30:42 +08:00
										 |  |  |     grid: { show: false }, | 
					
						
							| 
									
										
										
										
											2021-11-06 09:01:26 +08:00
										 |  |  |     ticks: { show: false }, | 
					
						
							| 
									
										
										
										
											2025-01-24 02:21:24 +08:00
										 |  |  |     gap: yAxisHidden ? 0 : 16, | 
					
						
							|  |  |  |     size: yAxisHidden ? 0 : yAxisWidth, | 
					
						
							| 
									
										
										
										
											2021-04-07 07:06:46 +08:00
										 |  |  |     theme, | 
					
						
							|  |  |  |   }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   let seriesIndex = 0; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   for (let i = 0; i < frame.fields.length; i++) { | 
					
						
							|  |  |  |     if (i === 0) { | 
					
						
							|  |  |  |       continue; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     const field = frame.fields[i]; | 
					
						
							| 
									
										
										
										
											2023-01-26 15:03:59 +08:00
										 |  |  |     const config: FieldConfig<PanelFieldConfig> = field.config; | 
					
						
							|  |  |  |     const customConfig: PanelFieldConfig = { | 
					
						
							| 
									
										
										
										
											2021-04-07 07:06:46 +08:00
										 |  |  |       ...defaultConfig, | 
					
						
							|  |  |  |       ...config.custom, | 
					
						
							|  |  |  |     }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     field.state!.seriesIndex = seriesIndex++; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-05-14 23:24:40 +08:00
										 |  |  |     // const scaleKey = config.unit || FIXED_UNIT;
 | 
					
						
							|  |  |  |     // const colorMode = getFieldColorModeForField(field);
 | 
					
						
							| 
									
										
										
										
											2021-04-07 07:06:46 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  |     builder.addSeries({ | 
					
						
							|  |  |  |       scaleKey: FIXED_UNIT, | 
					
						
							|  |  |  |       pathBuilder: coreConfig.drawPaths, | 
					
						
							|  |  |  |       pointsBuilder: coreConfig.drawPoints, | 
					
						
							|  |  |  |       //colorMode,
 | 
					
						
							| 
									
										
										
										
											2021-05-14 23:24:40 +08:00
										 |  |  |       lineWidth: customConfig.lineWidth, | 
					
						
							|  |  |  |       fillOpacity: customConfig.fillOpacity, | 
					
						
							| 
									
										
										
										
											2021-04-07 07:06:46 +08:00
										 |  |  |       theme, | 
					
						
							| 
									
										
										
										
											2021-05-07 03:22:03 +08:00
										 |  |  |       show: !customConfig.hideFrom?.viz, | 
					
						
							| 
									
										
										
										
											2021-04-07 07:06:46 +08:00
										 |  |  |       thresholds: config.thresholds, | 
					
						
							|  |  |  |       // The following properties are not used in the uPlot config, but are utilized as transport for legend config
 | 
					
						
							|  |  |  |       dataFrameFieldIndex: field.state?.origin, | 
					
						
							|  |  |  |     }); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   return builder; | 
					
						
							| 
									
										
										
										
											2021-05-05 16:44:31 +08:00
										 |  |  | }; | 
					
						
							| 
									
										
										
										
											2021-04-07 07:06:46 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-02-05 06:57:11 +08:00
										 |  |  | function getSpanNulls(field: Field) { | 
					
						
							|  |  |  |   let spanNulls = field.config.custom?.spanNulls; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   // magic value for join() to leave nulls alone instead of expanding null ranges
 | 
					
						
							|  |  |  |   // should be set to -1 when spanNulls = null|undefined|false|0, which is "retain nulls, without expanding"
 | 
					
						
							|  |  |  |   // Infinity is not optimal here since it causes spanNulls to be more expensive than simply removing all nulls unconditionally
 | 
					
						
							|  |  |  |   return !spanNulls ? -1 : spanNulls === true ? Infinity : spanNulls; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-09-01 23:43:57 +08:00
										 |  |  | /** | 
					
						
							|  |  |  |  * Merge values by the threshold | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | export function mergeThresholdValues(field: Field, theme: GrafanaTheme2): Field | undefined { | 
					
						
							|  |  |  |   const thresholds = field.config.thresholds; | 
					
						
							|  |  |  |   if (field.type !== FieldType.number || !thresholds || !thresholds.steps.length) { | 
					
						
							|  |  |  |     return undefined; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   const items = getThresholdItems(field.config, theme); | 
					
						
							|  |  |  |   if (items.length !== thresholds.steps.length) { | 
					
						
							|  |  |  |     return undefined; // should not happen
 | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   const thresholdToText = new Map<Threshold, string>(); | 
					
						
							|  |  |  |   const textToColor = new Map<string, string>(); | 
					
						
							|  |  |  |   for (let i = 0; i < items.length; i++) { | 
					
						
							|  |  |  |     thresholdToText.set(thresholds.steps[i], items[i].label); | 
					
						
							|  |  |  |     textToColor.set(items[i].label, items[i].color!); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-04-18 05:46:29 +08:00
										 |  |  |   let input = field.values; | 
					
						
							| 
									
										
										
										
											2021-09-01 23:43:57 +08:00
										 |  |  |   const vals = new Array<String | undefined>(field.values.length); | 
					
						
							|  |  |  |   if (thresholds.mode === ThresholdsMode.Percentage) { | 
					
						
							|  |  |  |     const { min, max } = getFieldConfigWithMinMax(field); | 
					
						
							|  |  |  |     const delta = max! - min!; | 
					
						
							|  |  |  |     input = input.map((v) => { | 
					
						
							|  |  |  |       if (v == null) { | 
					
						
							|  |  |  |         return v; | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |       return ((v - min!) / delta) * 100; | 
					
						
							|  |  |  |     }); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   for (let i = 0; i < vals.length; i++) { | 
					
						
							|  |  |  |     const v = input[i]; | 
					
						
							|  |  |  |     if (v == null) { | 
					
						
							|  |  |  |       vals[i] = v; | 
					
						
							|  |  |  |     } else { | 
					
						
							| 
									
										
										
										
											2022-02-03 00:25:49 +08:00
										 |  |  |       vals[i] = thresholdToText.get(getActiveThreshold(v, thresholds.steps)); | 
					
						
							| 
									
										
										
										
											2021-09-01 23:43:57 +08:00
										 |  |  |     } | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   return { | 
					
						
							|  |  |  |     ...field, | 
					
						
							| 
									
										
										
										
											2022-02-03 00:25:49 +08:00
										 |  |  |     config: { | 
					
						
							|  |  |  |       ...field.config, | 
					
						
							|  |  |  |       custom: { | 
					
						
							|  |  |  |         ...field.config.custom, | 
					
						
							| 
									
										
										
										
											2022-02-05 06:57:11 +08:00
										 |  |  |         spanNulls: getSpanNulls(field), | 
					
						
							| 
									
										
										
										
											2022-02-03 00:25:49 +08:00
										 |  |  |       }, | 
					
						
							|  |  |  |     }, | 
					
						
							| 
									
										
										
										
											2021-09-01 23:43:57 +08:00
										 |  |  |     type: FieldType.string, | 
					
						
							| 
									
										
										
										
											2023-04-18 05:46:29 +08:00
										 |  |  |     values: vals, | 
					
						
							| 
									
										
										
										
											2023-03-14 17:51:44 +08:00
										 |  |  |     display: (value) => ({ | 
					
						
							|  |  |  |       text: String(value), | 
					
						
							|  |  |  |       color: textToColor.get(String(value)), | 
					
						
							| 
									
										
										
										
											2021-09-01 23:43:57 +08:00
										 |  |  |       numeric: NaN, | 
					
						
							|  |  |  |     }), | 
					
						
							|  |  |  |   }; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-05-18 04:00:04 +08:00
										 |  |  | // This will return a set of frames with only graphable values included
 | 
					
						
							|  |  |  | export function prepareTimelineFields( | 
					
						
							|  |  |  |   series: DataFrame[] | undefined, | 
					
						
							| 
									
										
										
										
											2021-09-01 23:43:57 +08:00
										 |  |  |   mergeValues: boolean, | 
					
						
							| 
									
										
										
										
											2022-06-04 05:22:57 +08:00
										 |  |  |   timeRange: TimeRange, | 
					
						
							| 
									
										
										
										
											2021-09-01 23:43:57 +08:00
										 |  |  |   theme: GrafanaTheme2 | 
					
						
							| 
									
										
										
										
											2021-05-18 04:00:04 +08:00
										 |  |  | ): { frames?: DataFrame[]; warn?: string } { | 
					
						
							|  |  |  |   if (!series?.length) { | 
					
						
							|  |  |  |     return { warn: 'No data in response' }; | 
					
						
							|  |  |  |   } | 
					
						
							| 
									
										
										
										
											2024-02-17 14:38:13 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  |   cacheFieldDisplayNames(series); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-05-18 04:00:04 +08:00
										 |  |  |   let hasTimeseries = false; | 
					
						
							|  |  |  |   const frames: DataFrame[] = []; | 
					
						
							| 
									
										
										
										
											2022-06-04 05:22:57 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-05-18 04:00:04 +08:00
										 |  |  |   for (let frame of series) { | 
					
						
							| 
									
										
										
										
											2024-03-11 11:11:11 +08:00
										 |  |  |     let startFieldIdx = -1; | 
					
						
							|  |  |  |     let endFieldIdx = -1; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     for (let i = 0; i < frame.fields.length; i++) { | 
					
						
							|  |  |  |       let f = frame.fields[i]; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       if (f.type === FieldType.time) { | 
					
						
							|  |  |  |         if (startFieldIdx === -1) { | 
					
						
							|  |  |  |           startFieldIdx = i; | 
					
						
							|  |  |  |         } else if (endFieldIdx === -1) { | 
					
						
							|  |  |  |           endFieldIdx = i; | 
					
						
							|  |  |  |           break; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     let isTimeseries = startFieldIdx !== -1; | 
					
						
							| 
									
										
										
										
											2021-05-18 04:00:04 +08:00
										 |  |  |     let changed = false; | 
					
						
							| 
									
										
										
										
											2024-03-11 11:11:11 +08:00
										 |  |  |     frame = maybeSortFrame(frame, startFieldIdx); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // if we have a second time field, assume it is state end timestamps
 | 
					
						
							|  |  |  |     // and insert nulls into the data at the end timestamps
 | 
					
						
							|  |  |  |     if (endFieldIdx !== -1) { | 
					
						
							|  |  |  |       let startFrame: DataFrame = { | 
					
						
							|  |  |  |         ...frame, | 
					
						
							|  |  |  |         fields: frame.fields.filter((f, i) => i !== endFieldIdx), | 
					
						
							|  |  |  |       }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       let endFrame: DataFrame = { | 
					
						
							|  |  |  |         length: frame.length, | 
					
						
							|  |  |  |         fields: [frame.fields[endFieldIdx]], | 
					
						
							|  |  |  |       }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       frame = outerJoinDataFrames({ | 
					
						
							|  |  |  |         frames: [startFrame, endFrame], | 
					
						
							|  |  |  |         keepDisplayNames: true, | 
					
						
							|  |  |  |         nullMode: () => NULL_RETAIN, | 
					
						
							|  |  |  |       })!; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       frame.fields.forEach((f, i) => { | 
					
						
							|  |  |  |         if (i > 0) { | 
					
						
							|  |  |  |           let vals = f.values; | 
					
						
							|  |  |  |           for (let i = 0; i < vals.length; i++) { | 
					
						
							|  |  |  |             if (vals[i] == null) { | 
					
						
							|  |  |  |               vals[i] = null; | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |           } | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |       }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       changed = true; | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2022-06-04 05:22:57 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  |     let nulledFrame = applyNullInsertThreshold({ | 
					
						
							| 
									
										
										
										
											2024-03-11 11:11:11 +08:00
										 |  |  |       frame, | 
					
						
							| 
									
										
										
										
											2022-06-04 05:22:57 +08:00
										 |  |  |       refFieldPseudoMin: timeRange.from.valueOf(), | 
					
						
							|  |  |  |       refFieldPseudoMax: timeRange.to.valueOf(), | 
					
						
							|  |  |  |     }); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-06-17 06:28:41 +08:00
										 |  |  |     if (nulledFrame !== frame) { | 
					
						
							|  |  |  |       changed = true; | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2022-06-04 05:22:57 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-03-11 11:11:11 +08:00
										 |  |  |     frame = nullToValue(nulledFrame); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-05-18 04:00:04 +08:00
										 |  |  |     const fields: Field[] = []; | 
					
						
							| 
									
										
										
										
											2024-03-11 11:11:11 +08:00
										 |  |  |     for (let field of frame.fields) { | 
					
						
							| 
									
										
										
										
											2021-05-18 04:00:04 +08:00
										 |  |  |       switch (field.type) { | 
					
						
							|  |  |  |         case FieldType.time: | 
					
						
							|  |  |  |           isTimeseries = true; | 
					
						
							|  |  |  |           hasTimeseries = true; | 
					
						
							|  |  |  |           fields.push(field); | 
					
						
							|  |  |  |           break; | 
					
						
							| 
									
										
										
										
											2023-07-22 00:38:11 +08:00
										 |  |  |         case FieldType.enum: | 
					
						
							| 
									
										
										
										
											2021-05-18 04:00:04 +08:00
										 |  |  |         case FieldType.number: | 
					
						
							| 
									
										
										
										
											2021-09-01 23:43:57 +08:00
										 |  |  |           if (mergeValues && field.config.color?.mode === FieldColorModeId.Thresholds) { | 
					
						
							|  |  |  |             const f = mergeThresholdValues(field, theme); | 
					
						
							|  |  |  |             if (f) { | 
					
						
							|  |  |  |               fields.push(f); | 
					
						
							|  |  |  |               changed = true; | 
					
						
							|  |  |  |               continue; | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |           } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-05-18 04:00:04 +08:00
										 |  |  |         case FieldType.boolean: | 
					
						
							|  |  |  |         case FieldType.string: | 
					
						
							| 
									
										
										
										
											2021-08-19 02:43:53 +08:00
										 |  |  |           field = { | 
					
						
							|  |  |  |             ...field, | 
					
						
							|  |  |  |             config: { | 
					
						
							|  |  |  |               ...field.config, | 
					
						
							|  |  |  |               custom: { | 
					
						
							|  |  |  |                 ...field.config.custom, | 
					
						
							| 
									
										
										
										
											2022-02-05 06:57:11 +08:00
										 |  |  |                 spanNulls: getSpanNulls(field), | 
					
						
							| 
									
										
										
										
											2021-08-19 02:43:53 +08:00
										 |  |  |               }, | 
					
						
							|  |  |  |             }, | 
					
						
							|  |  |  |           }; | 
					
						
							| 
									
										
										
										
											2024-03-11 11:11:11 +08:00
										 |  |  |           changed = true; | 
					
						
							| 
									
										
										
										
											2021-05-18 04:00:04 +08:00
										 |  |  |           fields.push(field); | 
					
						
							|  |  |  |           break; | 
					
						
							|  |  |  |         default: | 
					
						
							|  |  |  |           changed = true; | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     if (isTimeseries && fields.length > 1) { | 
					
						
							|  |  |  |       hasTimeseries = true; | 
					
						
							|  |  |  |       if (changed) { | 
					
						
							|  |  |  |         frames.push({ | 
					
						
							| 
									
										
										
										
											2024-03-11 11:11:11 +08:00
										 |  |  |           ...frame, | 
					
						
							| 
									
										
										
										
											2021-05-18 04:00:04 +08:00
										 |  |  |           fields, | 
					
						
							|  |  |  |         }); | 
					
						
							|  |  |  |       } else { | 
					
						
							| 
									
										
										
										
											2024-03-11 11:11:11 +08:00
										 |  |  |         frames.push(frame); | 
					
						
							| 
									
										
										
										
											2021-05-18 04:00:04 +08:00
										 |  |  |       } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   if (!hasTimeseries) { | 
					
						
							|  |  |  |     return { warn: 'Data does not have a time field' }; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  |   if (!frames.length) { | 
					
						
							|  |  |  |     return { warn: 'No graphable fields' }; | 
					
						
							|  |  |  |   } | 
					
						
							| 
									
										
										
										
											2024-03-11 11:11:11 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-05-18 04:00:04 +08:00
										 |  |  |   return { frames }; | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2021-05-20 12:38:31 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-08-09 10:44:17 +08:00
										 |  |  | export function makeFramePerSeries(frames: DataFrame[]) { | 
					
						
							|  |  |  |   const outFrames: DataFrame[] = []; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   for (let frame of frames) { | 
					
						
							|  |  |  |     const timeFields = frame.fields.filter((field) => field.type === FieldType.time); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (timeFields.length > 0) { | 
					
						
							|  |  |  |       for (let field of frame.fields) { | 
					
						
							|  |  |  |         if (field.type !== FieldType.time) { | 
					
						
							|  |  |  |           outFrames.push({ fields: [...timeFields, field], length: frame.length }); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   return outFrames; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-09-27 23:02:03 +08:00
										 |  |  | export function getThresholdItems( | 
					
						
							|  |  |  |   fieldConfig: FieldConfig, | 
					
						
							|  |  |  |   theme: GrafanaTheme2, | 
					
						
							|  |  |  |   thresholdItems?: ThresholdsConfig | 
					
						
							|  |  |  | ): VizLegendItem[] { | 
					
						
							| 
									
										
										
										
											2021-09-01 23:43:57 +08:00
										 |  |  |   const items: VizLegendItem[] = []; | 
					
						
							| 
									
										
										
										
											2024-09-27 23:02:03 +08:00
										 |  |  |   const thresholds = thresholdItems ? thresholdItems : fieldConfig.thresholds; | 
					
						
							| 
									
										
										
										
											2021-09-01 23:43:57 +08:00
										 |  |  |   if (!thresholds || !thresholds.steps.length) { | 
					
						
							|  |  |  |     return items; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   const steps = thresholds.steps; | 
					
						
							| 
									
										
										
										
											2024-07-23 20:56:57 +08:00
										 |  |  |   const getDisplay = getValueFormat( | 
					
						
							|  |  |  |     thresholds.mode === ThresholdsMode.Percentage ? 'percent' : (fieldConfig.unit ?? '') | 
					
						
							|  |  |  |   ); | 
					
						
							| 
									
										
										
										
											2021-09-01 23:43:57 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-08-02 02:54:56 +08:00
										 |  |  |   // `undefined` value for decimals will use `auto`
 | 
					
						
							|  |  |  |   const format = (value: number) => formattedValueToString(getDisplay(value, fieldConfig.decimals ?? undefined)); | 
					
						
							| 
									
										
										
										
											2021-09-01 23:43:57 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-12-14 13:56:59 +08:00
										 |  |  |   for (let i = 0; i < steps.length; i++) { | 
					
						
							|  |  |  |     let step = steps[i]; | 
					
						
							|  |  |  |     let value = step.value; | 
					
						
							|  |  |  |     let pre = ''; | 
					
						
							|  |  |  |     let suf = ''; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (value === -Infinity && i < steps.length - 1) { | 
					
						
							|  |  |  |       value = steps[i + 1].value; | 
					
						
							|  |  |  |       pre = '< '; | 
					
						
							|  |  |  |     } else { | 
					
						
							|  |  |  |       suf = '+'; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-09-01 23:43:57 +08:00
										 |  |  |     items.push({ | 
					
						
							| 
									
										
										
										
											2023-08-02 02:54:56 +08:00
										 |  |  |       label: `${pre}${format(value)}${suf}`, | 
					
						
							| 
									
										
										
										
											2021-09-01 23:43:57 +08:00
										 |  |  |       color: theme.visualization.getColorByName(step.color), | 
					
						
							|  |  |  |       yAxis: 1, | 
					
						
							|  |  |  |     }); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   return items; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-09-27 23:02:03 +08:00
										 |  |  | export function getValueMappingItems(mappings: ValueMapping[], theme: GrafanaTheme2): VizLegendItem[] { | 
					
						
							|  |  |  |   const items: VizLegendItem[] = []; | 
					
						
							|  |  |  |   if (!mappings) { | 
					
						
							|  |  |  |     return items; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   for (let mapping of mappings) { | 
					
						
							|  |  |  |     const { options, type } = mapping; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (type === MappingType.ValueToText) { | 
					
						
							|  |  |  |       for (let [label, value] of Object.entries(options)) { | 
					
						
							|  |  |  |         const color = value.color; | 
					
						
							|  |  |  |         items.push({ | 
					
						
							|  |  |  |           label: label, | 
					
						
							|  |  |  |           color: theme.visualization.getColorByName(color ?? FALLBACK_COLOR), | 
					
						
							|  |  |  |           yAxis: 1, | 
					
						
							|  |  |  |         }); | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (type === MappingType.RangeToText) { | 
					
						
							|  |  |  |       const { from, result, to } = options; | 
					
						
							|  |  |  |       const { text, color } = result; | 
					
						
							|  |  |  |       const label = text ? `[${from} - ${to}] ${text}` : `[${from} - ${to}]`; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       items.push({ | 
					
						
							|  |  |  |         label: label, | 
					
						
							|  |  |  |         color: theme.visualization.getColorByName(color ?? FALLBACK_COLOR), | 
					
						
							|  |  |  |         yAxis: 1, | 
					
						
							|  |  |  |       }); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (type === MappingType.RegexToText) { | 
					
						
							|  |  |  |       const { pattern, result } = options; | 
					
						
							|  |  |  |       const { text, color } = result; | 
					
						
							|  |  |  |       const label = `${text || pattern}`; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       items.push({ | 
					
						
							|  |  |  |         label: label, | 
					
						
							|  |  |  |         color: theme.visualization.getColorByName(color ?? FALLBACK_COLOR), | 
					
						
							|  |  |  |         yAxis: 1, | 
					
						
							|  |  |  |       }); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (type === MappingType.SpecialValue) { | 
					
						
							|  |  |  |       const { match, result } = options; | 
					
						
							|  |  |  |       const { text, color } = result; | 
					
						
							|  |  |  |       const label = `${text || match}`; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       items.push({ | 
					
						
							|  |  |  |         label: label, | 
					
						
							|  |  |  |         color: theme.visualization.getColorByName(color ?? FALLBACK_COLOR), | 
					
						
							|  |  |  |         yAxis: 1, | 
					
						
							|  |  |  |       }); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   return items; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-05-20 12:38:31 +08:00
										 |  |  | export function prepareTimelineLegendItems( | 
					
						
							|  |  |  |   frames: DataFrame[] | undefined, | 
					
						
							| 
									
										
										
										
											2021-05-20 14:17:50 +08:00
										 |  |  |   options: VizLegendOptions, | 
					
						
							|  |  |  |   theme: GrafanaTheme2 | 
					
						
							| 
									
										
										
										
											2021-05-20 12:38:31 +08:00
										 |  |  | ): VizLegendItem[] | undefined { | 
					
						
							| 
									
										
										
										
											2022-07-28 03:39:55 +08:00
										 |  |  |   if (!frames || options.showLegend === false) { | 
					
						
							| 
									
										
										
										
											2021-05-20 12:38:31 +08:00
										 |  |  |     return undefined; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-12-24 02:04:41 +08:00
										 |  |  |   return getFieldLegendItem(allNonTimeFields(frames), theme); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | export function getFieldLegendItem(fields: Field[], theme: GrafanaTheme2): VizLegendItem[] | undefined { | 
					
						
							| 
									
										
										
										
											2021-05-20 12:38:31 +08:00
										 |  |  |   if (!fields.length) { | 
					
						
							|  |  |  |     return undefined; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   const items: VizLegendItem[] = []; | 
					
						
							| 
									
										
										
										
											2021-05-20 20:09:19 +08:00
										 |  |  |   const fieldConfig = fields[0].config; | 
					
						
							|  |  |  |   const colorMode = fieldConfig.color?.mode ?? FieldColorModeId.Fixed; | 
					
						
							| 
									
										
										
										
											2023-01-27 20:30:42 +08:00
										 |  |  |   const thresholds = fieldConfig.thresholds; | 
					
						
							| 
									
										
										
										
											2021-05-20 12:38:31 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  |   // If thresholds are enabled show each step in the legend
 | 
					
						
							| 
									
										
										
										
											2023-05-17 03:18:35 +08:00
										 |  |  |   // This ignores the hide from legend since the range is valid
 | 
					
						
							| 
									
										
										
										
											2023-01-27 20:30:42 +08:00
										 |  |  |   if (colorMode === FieldColorModeId.Thresholds && thresholds?.steps && thresholds.steps.length > 1) { | 
					
						
							| 
									
										
										
										
											2021-09-01 23:43:57 +08:00
										 |  |  |     return getThresholdItems(fieldConfig, theme); | 
					
						
							| 
									
										
										
										
											2021-05-20 12:38:31 +08:00
										 |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   // If thresholds are enabled show each step in the legend
 | 
					
						
							|  |  |  |   if (colorMode.startsWith('continuous')) { | 
					
						
							|  |  |  |     return undefined; // eventually a color bar
 | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-05-17 03:18:35 +08:00
										 |  |  |   const stateColors: Map<string, string | undefined> = new Map(); | 
					
						
							| 
									
										
										
										
											2021-05-20 12:38:31 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  |   fields.forEach((field) => { | 
					
						
							| 
									
										
										
										
											2023-05-17 03:18:35 +08:00
										 |  |  |     if (!field.config.custom?.hideFrom?.legend) { | 
					
						
							|  |  |  |       field.values.forEach((v) => { | 
					
						
							|  |  |  |         let state = field.display!(v); | 
					
						
							|  |  |  |         if (state.color) { | 
					
						
							|  |  |  |           stateColors.set(state.text, state.color!); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |       }); | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2021-05-20 12:38:31 +08:00
										 |  |  |   }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   stateColors.forEach((color, label) => { | 
					
						
							|  |  |  |     if (label.length > 0) { | 
					
						
							|  |  |  |       items.push({ | 
					
						
							|  |  |  |         label: label!, | 
					
						
							| 
									
										
										
										
											2021-05-20 14:17:50 +08:00
										 |  |  |         color: theme.visualization.getColorByName(color ?? FALLBACK_COLOR), | 
					
						
							| 
									
										
										
										
											2021-05-20 12:38:31 +08:00
										 |  |  |         yAxis: 1, | 
					
						
							|  |  |  |       }); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   return items; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | function allNonTimeFields(frames: DataFrame[]): Field[] { | 
					
						
							|  |  |  |   const fields: Field[] = []; | 
					
						
							|  |  |  |   for (const frame of frames) { | 
					
						
							|  |  |  |     for (const field of frame.fields) { | 
					
						
							|  |  |  |       if (field.type !== FieldType.time) { | 
					
						
							|  |  |  |         fields.push(field); | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  |   return fields; | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2021-06-03 10:43:47 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | export function findNextStateIndex(field: Field, datapointIdx: number) { | 
					
						
							|  |  |  |   let end; | 
					
						
							|  |  |  |   let rightPointer = datapointIdx + 1; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-06-16 12:35:44 +08:00
										 |  |  |   if (rightPointer >= field.values.length) { | 
					
						
							| 
									
										
										
										
											2021-06-03 10:43:47 +08:00
										 |  |  |     return null; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-04-20 22:59:18 +08:00
										 |  |  |   const startValue = field.values[datapointIdx]; | 
					
						
							| 
									
										
										
										
											2022-03-01 04:16:30 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-06-03 10:43:47 +08:00
										 |  |  |   while (end === undefined) { | 
					
						
							| 
									
										
										
										
											2021-06-16 12:35:44 +08:00
										 |  |  |     if (rightPointer >= field.values.length) { | 
					
						
							| 
									
										
										
										
											2021-06-03 10:43:47 +08:00
										 |  |  |       return null; | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2023-04-20 22:59:18 +08:00
										 |  |  |     const rightValue = field.values[rightPointer]; | 
					
						
							| 
									
										
										
										
											2021-06-03 10:43:47 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-03-01 04:16:30 +08:00
										 |  |  |     if (rightValue === undefined || rightValue === startValue) { | 
					
						
							| 
									
										
										
										
											2021-06-03 10:43:47 +08:00
										 |  |  |       rightPointer++; | 
					
						
							| 
									
										
										
										
											2022-03-01 04:16:30 +08:00
										 |  |  |     } else { | 
					
						
							|  |  |  |       end = rightPointer; | 
					
						
							| 
									
										
										
										
											2021-06-03 10:43:47 +08:00
										 |  |  |     } | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   return end; | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2022-01-24 20:32:55 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | /** | 
					
						
							|  |  |  |  * Returns the precise duration of a time range passed in milliseconds. | 
					
						
							|  |  |  |  * This function calculates with 30 days month and 365 days year. | 
					
						
							|  |  |  |  * adapted from https://gist.github.com/remino/1563878
 | 
					
						
							|  |  |  |  * @param milliSeconds The duration in milliseconds | 
					
						
							|  |  |  |  * @returns A formated string of the duration | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | export function fmtDuration(milliSeconds: number): string { | 
					
						
							|  |  |  |   if (milliSeconds < 0 || Number.isNaN(milliSeconds)) { | 
					
						
							|  |  |  |     return ''; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   let yr: number, mo: number, wk: number, d: number, h: number, m: number, s: number, ms: number; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   s = Math.floor(milliSeconds / 1000); | 
					
						
							|  |  |  |   m = Math.floor(s / 60); | 
					
						
							|  |  |  |   s = s % 60; | 
					
						
							|  |  |  |   h = Math.floor(m / 60); | 
					
						
							|  |  |  |   m = m % 60; | 
					
						
							|  |  |  |   d = Math.floor(h / 24); | 
					
						
							|  |  |  |   h = h % 24; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   yr = Math.floor(d / 365); | 
					
						
							|  |  |  |   if (yr > 0) { | 
					
						
							|  |  |  |     d = d % 365; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   mo = Math.floor(d / 30); | 
					
						
							|  |  |  |   if (mo > 0) { | 
					
						
							|  |  |  |     d = d % 30; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   wk = Math.floor(d / 7); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   if (wk > 0) { | 
					
						
							|  |  |  |     d = d % 7; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   ms = Math.round((milliSeconds % 1000) * 1000) / 1000; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-02-02 20:02:32 +08:00
										 |  |  |   return ( | 
					
						
							|  |  |  |     yr > 0 | 
					
						
							|  |  |  |       ? yr + 'y ' + (mo > 0 ? mo + 'mo ' : '') + (wk > 0 ? wk + 'w ' : '') + (d > 0 ? d + 'd ' : '') | 
					
						
							|  |  |  |       : mo > 0 | 
					
						
							| 
									
										
										
										
											2024-01-23 19:41:24 +08:00
										 |  |  |         ? mo + 'mo ' + (wk > 0 ? wk + 'w ' : '') + (d > 0 ? d + 'd ' : '') | 
					
						
							|  |  |  |         : wk > 0 | 
					
						
							|  |  |  |           ? wk + 'w ' + (d > 0 ? d + 'd ' : '') | 
					
						
							|  |  |  |           : d > 0 | 
					
						
							|  |  |  |             ? d + 'd ' + (h > 0 ? h + 'h ' : '') | 
					
						
							|  |  |  |             : h > 0 | 
					
						
							|  |  |  |               ? h + 'h ' + (m > 0 ? m + 'm ' : '') | 
					
						
							|  |  |  |               : m > 0 | 
					
						
							|  |  |  |                 ? m + 'm ' + (s > 0 ? s + 's ' : '') | 
					
						
							|  |  |  |                 : s > 0 | 
					
						
							|  |  |  |                   ? s + 's ' + (ms > 0 ? ms + 'ms ' : '') | 
					
						
							|  |  |  |                   : ms > 0 | 
					
						
							|  |  |  |                     ? ms + 'ms ' | 
					
						
							|  |  |  |                     : '0' | 
					
						
							| 
									
										
										
										
											2022-01-24 20:32:55 +08:00
										 |  |  |   ).trim(); | 
					
						
							|  |  |  | } |