{"version":3,"file":"utils.cjs","sources":["../../../src/dataframe/utils.ts"],"sourcesContent":["import { GrafanaTheme2 } from '../themes/types';\nimport { DataFrame, Field, FieldType } from '../types/dataFrame';\nimport { TimeRange } from '../types/time';\n\nimport { getTimeField } from './processDataFrame';\n\nconst MAX_TIME_COMPARISONS = 100;\n\nexport function isTimeSeriesFrame(frame: DataFrame) {\n  // If we have less than two frames we can't have a timeseries\n  if (frame.fields.length < 2) {\n    return false;\n  }\n\n  // Find a number field, as long as we have any number field this should work\n  const numberField = frame.fields.find((field) => field.type === FieldType.number);\n\n  // There are certain query types in which we will\n  // get times but they will be the same or not be\n  // in increasing order. To have a time-series the\n  // times need to be ordered from past to present\n  let timeFieldFound = false;\n  for (const field of frame.fields) {\n    if (isTimeSeriesField(field)) {\n      timeFieldFound = true;\n      break;\n    }\n  }\n\n  return timeFieldFound && numberField !== undefined;\n}\n\nexport function isTimeSeriesFrames(data: DataFrame[]) {\n  return !data.find((frame) => !isTimeSeriesFrame(frame));\n}\n\n/**\n * Determines if a field is a time field in ascending\n * order within the sampling range specified by\n * MAX_TIME_COMPARISONS\n *\n * @param field\n * @returns boolean\n */\nexport function isTimeSeriesField(field: Field) {\n  if (field.type !== FieldType.time) {\n    return false;\n  }\n\n  let greatestTime: number | null = null;\n  let testWindow = field.values.length > MAX_TIME_COMPARISONS ? MAX_TIME_COMPARISONS : field.values.length;\n\n  // Test up to the test window number of values\n  for (let i = 0; i < testWindow; i++) {\n    const time = field.values[i];\n\n    // Check to see if the current time is greater than\n    // the last time. If we get to the end then we\n    // have a time series otherwise we return false\n    if (greatestTime === null || (time !== null && time > greatestTime)) {\n      greatestTime = time;\n    } else {\n      return false;\n    }\n  }\n\n  return true;\n}\n\n/**\n * Indicates if there is any time field in the array of data frames\n * @param data\n */\nexport function anySeriesWithTimeField(data: DataFrame[]) {\n  for (let i = 0; i < data.length; i++) {\n    const timeField = getTimeField(data[i]);\n    if (timeField.timeField !== undefined && timeField.timeIndex !== undefined) {\n      return true;\n    }\n  }\n  return false;\n}\n\n/**\n * Indicates if there is any time field in the data frame\n * @param data\n */\nexport function hasTimeField(data: DataFrame): boolean {\n  return data.fields.some((field) => field.type === FieldType.time);\n}\n\n/**\n * Get row id based on the meta.uniqueRowIdFields attribute.\n * @param dataFrame\n * @param rowIndex\n */\nexport function getRowUniqueId(dataFrame: DataFrame, rowIndex: number) {\n  if (dataFrame.meta?.uniqueRowIdFields === undefined) {\n    return undefined;\n  }\n  return dataFrame.meta.uniqueRowIdFields.map((fieldIndex) => dataFrame.fields[fieldIndex].values[rowIndex]).join('-');\n}\n\n/**\n * Simple helper to add values to a data frame. Doesn't do any validation so make sure you are adding the right types\n * of values.\n * @param dataFrame\n * @param row Either an array of values or an object with keys that match the field names.\n */\nexport function addRow(dataFrame: DataFrame, row: Record<string, unknown> | unknown[]) {\n  if (row instanceof Array) {\n    for (let i = 0; i < row.length; i++) {\n      dataFrame.fields[i].values.push(row[i]);\n    }\n  } else {\n    for (const field of dataFrame.fields) {\n      field.values.push(row[field.name]);\n    }\n  }\n  try {\n    dataFrame.length++;\n  } catch (e) {\n    // Unfortunate but even though DataFrame as interface defines length some implementation of DataFrame only have\n    // length getter. In that case it will throw and so we just skip and assume they defined a `getter` for length that\n    // does not need any external updating.\n  }\n}\n\n/**\n * Aligns time range comparison data by adjusting timestamps and applying compare-specific styling\n * @param series - The DataFrame containing the comparison data\n * @param diff - The time difference in milliseconds to align the timestamps\n * @param theme - The Grafana theme for color calculations\n */\nexport function alignTimeRangeCompareData(series: DataFrame, diff: number, theme: GrafanaTheme2) {\n  series.fields.forEach((field: Field) => {\n    // Align compare series time stamps with reference series\n    if (field.type === FieldType.time) {\n      field.values = field.values.map((v: number) => {\n        return diff < 0 ? v - diff : v + diff;\n      });\n    }\n\n    field.config = {\n      ...(field.config ?? {}),\n      custom: {\n        ...(field.config?.custom ?? {}),\n        timeCompare: {\n          diffMs: diff,\n          isTimeShiftQuery: true,\n        },\n      },\n    };\n\n    // Apply visual styling for comparison series\n    if (field.type === FieldType.number || field.type === FieldType.boolean || field.type === FieldType.enum) {\n      field.config.custom = {\n        ...(field.config.custom ?? {}),\n        lineStyle: {\n          fill: 'dash',\n          dash: [1, 5, 4, 5],\n        },\n      };\n    }\n  });\n}\n\n/**\n * Checks if a time comparison frame needs alignment based on whether its first time is before the current time range.\n * Returns true if the first time in compare is before timeRange.from, indicating it needs shifting.\n * @param compareFrame - The frame with time comparison data\n * @param allFrames - Array of all frames to find the matching original frame\n * @param timeRange - The current panel time range\n * @returns true if alignment is needed\n */\nexport function shouldAlignTimeCompare(compareFrame: DataFrame, allFrames: DataFrame[], timeRange: TimeRange): boolean {\n  // Find the matching original frame by removing '-compare' from refId\n  const compareRefId = compareFrame.refId;\n  if (!compareRefId || !compareRefId.endsWith('-compare')) {\n    return false;\n  }\n\n  const originalRefId = compareRefId.replace('-compare', '');\n  const originalFrame = allFrames.find(\n    (frame) => frame.refId === originalRefId && !frame.meta?.timeCompare?.isTimeShiftQuery\n  );\n\n  if (!originalFrame) {\n    return false;\n  }\n\n  // Find time fields\n  const compareTimeField = compareFrame.fields.find((field) => field.type === FieldType.time);\n  const originalTimeField = originalFrame.fields.find((field) => field.type === FieldType.time);\n\n  if (!compareTimeField?.values.length || !originalTimeField?.values.length) {\n    return false;\n  }\n\n  // Find first non-null time value from each frame\n  const compareFirstTime = compareTimeField.values.find((value) => value != null);\n  const originalFirstTime = originalTimeField.values.find((value) => value != null);\n\n  if (compareFirstTime == null || originalFirstTime == null) {\n    return false;\n  }\n\n  // Check if first non-null time value is before timeRange.from\n  return compareFirstTime < timeRange.from.valueOf();\n}\n"],"names":["FieldType","getTimeField"],"mappings":"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"}