| 
									
										
										
										
											2021-04-07 07:06:46 +08:00
										 |  |  | import React from 'react'; | 
					
						
							| 
									
										
										
										
											2022-04-22 21:33:13 +08:00
										 |  |  | import uPlot from 'uplot'; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-04-07 07:06:46 +08:00
										 |  |  | import { | 
					
						
							|  |  |  |   DataFrame, | 
					
						
							| 
									
										
										
										
											2021-11-06 01:52:40 +08:00
										 |  |  |   DashboardCursorSync, | 
					
						
							|  |  |  |   DataHoverPayload, | 
					
						
							|  |  |  |   DataHoverEvent, | 
					
						
							|  |  |  |   DataHoverClearEvent, | 
					
						
							| 
									
										
										
										
											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, | 
					
						
							| 
									
										
										
										
											2021-04-07 07:06:46 +08:00
										 |  |  | } from '@grafana/data'; | 
					
						
							| 
									
										
										
										
											2022-07-04 16:11:33 +08:00
										 |  |  | import { maybeSortFrame } from '@grafana/data/src/transformations/transformers/joinDataFrames'; | 
					
						
							| 
									
										
										
										
											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'; | 
					
						
							| 
									
										
										
										
											2021-05-20 12:38:31 +08:00
										 |  |  | import { | 
					
						
							|  |  |  |   FIXED_UNIT, | 
					
						
							|  |  |  |   SeriesVisibilityChangeMode, | 
					
						
							| 
									
										
										
										
											2021-06-16 12:35:44 +08:00
										 |  |  |   UPlotConfigBuilder, | 
					
						
							| 
									
										
										
										
											2021-05-20 12:38:31 +08:00
										 |  |  |   UPlotConfigPrepFn, | 
					
						
							|  |  |  |   VizLegendItem, | 
					
						
							|  |  |  | } from '@grafana/ui'; | 
					
						
							| 
									
										
										
										
											2022-06-04 05:22:57 +08:00
										 |  |  | import { applyNullInsertThreshold } from '@grafana/ui/src/components/GraphNG/nullInsertThreshold'; | 
					
						
							|  |  |  | import { nullToValue } from '@grafana/ui/src/components/GraphNG/nullToValue'; | 
					
						
							| 
									
										
										
										
											2021-06-03 10:43:47 +08:00
										 |  |  | import { PlotTooltipInterpolator } from '@grafana/ui/src/components/uPlot/types'; | 
					
						
							| 
									
										
										
										
											2022-08-03 21:47:09 +08:00
										 |  |  | import { preparePlotData2, getStackingGroups } from '@grafana/ui/src/components/uPlot/utils'; | 
					
						
							| 
									
										
										
										
											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; | 
					
						
							|  |  |  |   sync?: () => DashboardCursorSync; | 
					
						
							|  |  |  |   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; | 
					
						
							| 
									
										
										
										
											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, | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-05-07 03:22:03 +08:00
										 |  |  | export function mapMouseEventToMode(event: React.MouseEvent): SeriesVisibilityChangeMode { | 
					
						
							| 
									
										
										
										
											2021-04-07 07:06:46 +08:00
										 |  |  |   if (event.ctrlKey || event.metaKey || event.shiftKey) { | 
					
						
							| 
									
										
										
										
											2021-05-07 03:22:03 +08:00
										 |  |  |     return SeriesVisibilityChangeMode.AppendToSelection; | 
					
						
							| 
									
										
										
										
											2021-04-07 07:06:46 +08:00
										 |  |  |   } | 
					
						
							| 
									
										
										
										
											2021-05-07 03:22:03 +08:00
										 |  |  |   return SeriesVisibilityChangeMode.ToggleSelection; | 
					
						
							| 
									
										
										
										
											2021-04-07 07:06:46 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											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, | 
					
						
							| 
									
										
										
										
											2021-11-06 01:52:40 +08:00
										 |  |  |   eventBus, | 
					
						
							|  |  |  |   sync, | 
					
						
							| 
									
										
										
										
											2021-05-05 16:44:31 +08:00
										 |  |  |   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, | 
					
						
							| 
									
										
										
										
											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 xScaleUnit = 'time'; | 
					
						
							|  |  |  |   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)), | 
					
						
							| 
									
										
										
										
											2021-06-03 10:43:47 +08:00
										 |  |  |     onHover: (seriesIndex, valueIndex) => { | 
					
						
							|  |  |  |       hoveredSeriesIdx = seriesIndex; | 
					
						
							|  |  |  |       hoveredDataIdx = valueIndex; | 
					
						
							| 
									
										
										
										
											2021-06-03 17:46:32 +08:00
										 |  |  |       shouldChangeHover = true; | 
					
						
							| 
									
										
										
										
											2021-04-07 07:06:46 +08:00
										 |  |  |     }, | 
					
						
							| 
									
										
										
										
											2021-06-03 10:43:47 +08:00
										 |  |  |     onLeave: () => { | 
					
						
							|  |  |  |       hoveredSeriesIdx = null; | 
					
						
							|  |  |  |       hoveredDataIdx = null; | 
					
						
							| 
									
										
										
										
											2021-06-03 17:46:32 +08:00
										 |  |  |       shouldChangeHover = true; | 
					
						
							| 
									
										
										
										
											2021-04-07 07:06:46 +08:00
										 |  |  |     }, | 
					
						
							|  |  |  |   }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-06-03 17:46:32 +08:00
										 |  |  |   let shouldChangeHover = false; | 
					
						
							| 
									
										
										
										
											2021-06-03 10:43:47 +08:00
										 |  |  |   let hoveredSeriesIdx: number | null = null; | 
					
						
							|  |  |  |   let hoveredDataIdx: number | null = null; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-04-07 07:06:46 +08:00
										 |  |  |   const coreConfig = getConfig(opts); | 
					
						
							| 
									
										
										
										
											2021-11-06 01:52:40 +08:00
										 |  |  |   const payload: DataHoverPayload = { | 
					
						
							|  |  |  |     point: { | 
					
						
							|  |  |  |       [xScaleUnit]: null, | 
					
						
							|  |  |  |       [FIXED_UNIT]: null, | 
					
						
							|  |  |  |     }, | 
					
						
							|  |  |  |     data: frame, | 
					
						
							|  |  |  |   }; | 
					
						
							| 
									
										
										
										
											2021-04-07 07:06:46 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  |   builder.addHook('init', coreConfig.init); | 
					
						
							|  |  |  |   builder.addHook('drawClear', coreConfig.drawClear); | 
					
						
							|  |  |  |   builder.addHook('setCursor', coreConfig.setCursor); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-06-03 10:43:47 +08:00
										 |  |  |   // in TooltipPlugin, this gets invoked and the result is bound to a setCursor hook
 | 
					
						
							|  |  |  |   // which fires after the above setCursor hook, so can take advantage of hoveringOver
 | 
					
						
							|  |  |  |   // already set by the above onHover/onLeave callbacks that fire from coreConfig.setCursor
 | 
					
						
							|  |  |  |   const interpolateTooltip: PlotTooltipInterpolator = ( | 
					
						
							|  |  |  |     updateActiveSeriesIdx, | 
					
						
							|  |  |  |     updateActiveDatapointIdx, | 
					
						
							|  |  |  |     updateTooltipPosition | 
					
						
							| 
									
										
										
										
											2021-08-19 02:43:53 +08:00
										 |  |  |   ) => { | 
					
						
							| 
									
										
										
										
											2021-06-03 17:46:32 +08:00
										 |  |  |     if (shouldChangeHover) { | 
					
						
							|  |  |  |       if (hoveredSeriesIdx != null) { | 
					
						
							|  |  |  |         updateActiveSeriesIdx(hoveredSeriesIdx); | 
					
						
							|  |  |  |         updateActiveDatapointIdx(hoveredDataIdx); | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       shouldChangeHover = false; | 
					
						
							| 
									
										
										
										
											2021-06-03 10:43:47 +08:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2021-06-03 17:46:32 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  |     updateTooltipPosition(hoveredSeriesIdx == null); | 
					
						
							| 
									
										
										
										
											2021-06-03 10:43:47 +08:00
										 |  |  |   }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   builder.setTooltipInterpolator(interpolateTooltip); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											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, | 
					
						
							|  |  |  |   }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   builder.addAxis({ | 
					
						
							| 
									
										
										
										
											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, | 
					
						
							| 
									
										
										
										
											2021-08-28 01:30:42 +08:00
										 |  |  |     grid: { show: true }, | 
					
						
							| 
									
										
										
										
											2021-04-07 07:06:46 +08:00
										 |  |  |   }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   builder.addAxis({ | 
					
						
							|  |  |  |     scaleKey: FIXED_UNIT, // y
 | 
					
						
							|  |  |  |     isTime: false, | 
					
						
							|  |  |  |     placement: AxisPlacement.Left, | 
					
						
							|  |  |  |     splits: coreConfig.ySplits, | 
					
						
							|  |  |  |     values: coreConfig.yValues, | 
					
						
							| 
									
										
										
										
											2021-08-28 01:30:42 +08:00
										 |  |  |     grid: { show: false }, | 
					
						
							| 
									
										
										
										
											2021-11-06 09:01:26 +08:00
										 |  |  |     ticks: { show: false }, | 
					
						
							| 
									
										
										
										
											2021-04-07 07:06:46 +08:00
										 |  |  |     gap: 16, | 
					
						
							|  |  |  |     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, | 
					
						
							|  |  |  |     }); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-01-20 23:38:10 +08:00
										 |  |  |   if (sync && sync() !== DashboardCursorSync.Off) { | 
					
						
							| 
									
										
										
										
											2021-11-06 01:52:40 +08:00
										 |  |  |     let cursor: Partial<uPlot.Cursor> = {}; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     cursor.sync = { | 
					
						
							|  |  |  |       key: '__global_', | 
					
						
							|  |  |  |       filters: { | 
					
						
							|  |  |  |         pub: (type: string, src: uPlot, x: number, y: number, w: number, h: number, dataIdx: number) => { | 
					
						
							| 
									
										
										
										
											2022-01-20 23:38:10 +08:00
										 |  |  |           if (sync && sync() === DashboardCursorSync.Off) { | 
					
						
							|  |  |  |             return false; | 
					
						
							|  |  |  |           } | 
					
						
							| 
									
										
										
										
											2021-11-06 01:52:40 +08:00
										 |  |  |           payload.rowIndex = dataIdx; | 
					
						
							|  |  |  |           if (x < 0 && y < 0) { | 
					
						
							|  |  |  |             payload.point[xScaleUnit] = null; | 
					
						
							|  |  |  |             payload.point[FIXED_UNIT] = null; | 
					
						
							|  |  |  |             eventBus.publish(new DataHoverClearEvent()); | 
					
						
							|  |  |  |           } else { | 
					
						
							|  |  |  |             payload.point[xScaleUnit] = src.posToVal(x, xScaleKey); | 
					
						
							|  |  |  |             payload.point.panelRelY = y > 0 ? y / h : 1; // used for old graph panel to position tooltip
 | 
					
						
							|  |  |  |             payload.down = undefined; | 
					
						
							|  |  |  |             eventBus.publish(new DataHoverEvent(payload)); | 
					
						
							|  |  |  |           } | 
					
						
							|  |  |  |           return true; | 
					
						
							|  |  |  |         }, | 
					
						
							|  |  |  |       }, | 
					
						
							| 
									
										
										
										
											2022-11-29 23:52:08 +08:00
										 |  |  |       scales: [xScaleKey, null], | 
					
						
							| 
									
										
										
										
											2021-11-06 01:52:40 +08:00
										 |  |  |     }; | 
					
						
							|  |  |  |     builder.setSync(); | 
					
						
							|  |  |  |     builder.setCursor(cursor); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-04-07 07:06:46 +08:00
										 |  |  |   return builder; | 
					
						
							| 
									
										
										
										
											2021-05-05 16:44:31 +08:00
										 |  |  | }; | 
					
						
							| 
									
										
										
										
											2021-04-07 07:06:46 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | export function getNamesToFieldIndex(frame: DataFrame): Map<string, number> { | 
					
						
							|  |  |  |   const names = new Map<string, number>(); | 
					
						
							|  |  |  |   for (let i = 0; i < frame.fields.length; i++) { | 
					
						
							|  |  |  |     names.set(getFieldDisplayName(frame.fields[i], frame), i); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  |   return names; | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2021-05-18 04:00:04 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | /** | 
					
						
							|  |  |  |  * If any sequential duplicate values exist, this will return a new array | 
					
						
							|  |  |  |  * with the future values set to undefined. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * in:  1,        1,undefined,        1,2,        2,null,2,3 | 
					
						
							|  |  |  |  * out: 1,undefined,undefined,undefined,2,undefined,null,2,3 | 
					
						
							|  |  |  |  */ | 
					
						
							| 
									
										
										
										
											2022-11-29 23:52:08 +08:00
										 |  |  | export function unsetSameFutureValues(values: unknown[]): unknown[] | undefined { | 
					
						
							| 
									
										
										
										
											2021-05-18 04:00:04 +08:00
										 |  |  |   let prevVal = values[0]; | 
					
						
							| 
									
										
										
										
											2022-11-29 23:52:08 +08:00
										 |  |  |   let clone: unknown[] | undefined = undefined; | 
					
						
							| 
									
										
										
										
											2021-05-18 04:00:04 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  |   for (let i = 1; i < values.length; i++) { | 
					
						
							|  |  |  |     let value = values[i]; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (value === null) { | 
					
						
							|  |  |  |       prevVal = null; | 
					
						
							|  |  |  |     } else { | 
					
						
							|  |  |  |       if (value === prevVal) { | 
					
						
							|  |  |  |         if (!clone) { | 
					
						
							|  |  |  |           clone = [...values]; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         clone[i] = undefined; | 
					
						
							|  |  |  |       } else if (value != null) { | 
					
						
							|  |  |  |         prevVal = value; | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  |   return clone; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											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' }; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  |   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) { | 
					
						
							|  |  |  |     let isTimeseries = false; | 
					
						
							|  |  |  |     let changed = false; | 
					
						
							| 
									
										
										
										
											2022-07-04 16:11:33 +08:00
										 |  |  |     let maybeSortedFrame = maybeSortFrame( | 
					
						
							|  |  |  |       frame, | 
					
						
							|  |  |  |       frame.fields.findIndex((f) => f.type === FieldType.time) | 
					
						
							|  |  |  |     ); | 
					
						
							| 
									
										
										
										
											2022-06-04 05:22:57 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  |     let nulledFrame = applyNullInsertThreshold({ | 
					
						
							| 
									
										
										
										
											2022-07-04 16:11:33 +08:00
										 |  |  |       frame: maybeSortedFrame, | 
					
						
							| 
									
										
										
										
											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
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-05-18 04:00:04 +08:00
										 |  |  |     const fields: Field[] = []; | 
					
						
							| 
									
										
										
										
											2022-06-04 05:22:57 +08:00
										 |  |  |     for (let field of nullToValue(nulledFrame).fields) { | 
					
						
							| 
									
										
										
										
											2023-05-17 03:18:35 +08:00
										 |  |  |       if (field.config.custom?.hideFrom?.viz) { | 
					
						
							|  |  |  |         continue; | 
					
						
							|  |  |  |       } | 
					
						
							| 
									
										
										
										
											2021-05-18 04:00:04 +08:00
										 |  |  |       switch (field.type) { | 
					
						
							|  |  |  |         case FieldType.time: | 
					
						
							|  |  |  |           isTimeseries = true; | 
					
						
							|  |  |  |           hasTimeseries = true; | 
					
						
							|  |  |  |           fields.push(field); | 
					
						
							|  |  |  |           break; | 
					
						
							|  |  |  |         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
										 |  |  |               }, | 
					
						
							|  |  |  |             }, | 
					
						
							|  |  |  |           }; | 
					
						
							| 
									
										
										
										
											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({ | 
					
						
							| 
									
										
										
										
											2022-07-04 16:11:33 +08:00
										 |  |  |           ...maybeSortedFrame, | 
					
						
							| 
									
										
										
										
											2021-05-18 04:00:04 +08:00
										 |  |  |           fields, | 
					
						
							|  |  |  |         }); | 
					
						
							|  |  |  |       } else { | 
					
						
							| 
									
										
										
										
											2022-07-04 16:11:33 +08:00
										 |  |  |         frames.push(maybeSortedFrame); | 
					
						
							| 
									
										
										
										
											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' }; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  |   return { frames }; | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2021-05-20 12:38:31 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-09-01 23:43:57 +08:00
										 |  |  | export function getThresholdItems(fieldConfig: FieldConfig, theme: GrafanaTheme2): VizLegendItem[] { | 
					
						
							|  |  |  |   const items: VizLegendItem[] = []; | 
					
						
							|  |  |  |   const thresholds = fieldConfig.thresholds; | 
					
						
							|  |  |  |   if (!thresholds || !thresholds.steps.length) { | 
					
						
							|  |  |  |     return items; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   const steps = thresholds.steps; | 
					
						
							|  |  |  |   const disp = getValueFormat(thresholds.mode === ThresholdsMode.Percentage ? 'percent' : fieldConfig.unit ?? ''); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   const fmt = (v: number) => formattedValueToString(disp(v)); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											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({ | 
					
						
							| 
									
										
										
										
											2022-12-14 13:56:59 +08:00
										 |  |  |       label: `${pre}${fmt(value)}${suf}`, | 
					
						
							| 
									
										
										
										
											2021-09-01 23:43:57 +08:00
										 |  |  |       color: theme.visualization.getColorByName(step.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 | 
					
						
							|  |  |  |       ? 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(); | 
					
						
							|  |  |  | } |