{"version":3,"file":"displayProcessor.cjs","sources":["../../../src/field/displayProcessor.ts"],"sourcesContent":["// Libraries\nimport { toString, toNumber as _toNumber, isEmpty, isBoolean, isArray, join } from 'lodash';\n\n// Types\nimport { getFieldTypeFromValue } from '../dataframe/processDataFrame';\nimport { toUtc } from '../datetime/moment_wrapper';\nimport { dateTimeParse } from '../datetime/parser';\nimport { GrafanaTheme2 } from '../themes/types';\nimport { KeyValue } from '../types/data';\nimport { Field, FieldType } from '../types/dataFrame';\nimport { DecimalCount, DisplayProcessor, DisplayValue } from '../types/displayValue';\nimport { TimeZone } from '../types/time';\nimport { anyToNumber } from '../utils/anyToNumber';\nimport { getValueMappingResult } from '../utils/valueMappings';\nimport { FormattedValue, getValueFormat, isBooleanUnit } from '../valueFormats/valueFormats';\n\nimport { getScaleCalculator } from './scale';\n\ninterface DisplayProcessorOptions {\n  field: Partial<Field>;\n  /**\n   * Will pick browser timezone if not defined\n   */\n  timeZone?: TimeZone;\n  /**\n   * Will pick 'dark' if not defined\n   */\n  theme: GrafanaTheme2;\n}\n\n// Reasonable units for time\nconst timeFormats: KeyValue<boolean> = {\n  dateTimeAsIso: true,\n  dateTimeAsIsoNoDateIfToday: true,\n  dateTimeAsUS: true,\n  dateTimeAsUSNoDateIfToday: true,\n  dateTimeAsLocal: true,\n  dateTimeAsLocalNoDateIfToday: true,\n  dateTimeFromNow: true,\n};\n\nexport function getDisplayProcessor(options?: DisplayProcessorOptions): DisplayProcessor {\n  if (!options || isEmpty(options) || !options.field) {\n    return toStringProcessor;\n  }\n\n  const field = options.field as Field;\n  const config = field.config ?? {};\n  const { palette } = options.theme.visualization;\n\n  let unit = config.unit;\n  let hasDateUnit = Boolean(unit && (timeFormats[unit] || unit.startsWith('time:')));\n  let showMs = false;\n\n  if (field.type === FieldType.time && !hasDateUnit) {\n    unit = `dateTimeAsSystem`;\n    hasDateUnit = true;\n    if (field.values && field.values.length > 1) {\n      let start = field.values[0];\n      let end = field.values[field.values.length - 1];\n      if (typeof start === 'string') {\n        start = dateTimeParse(start).unix();\n        end = dateTimeParse(end).unix();\n      } else {\n        start /= 1e3;\n        end /= 1e3;\n      }\n      showMs = Math.abs(end - start) < 60; //show ms when minute or less\n    }\n  } else if (field.type === FieldType.boolean) {\n    if (!isBooleanUnit(unit)) {\n      unit = 'bool';\n    }\n  } else if (!unit && field.type === FieldType.string) {\n    unit = 'string';\n  }\n\n  const hasCurrencyUnit = unit?.startsWith('currency');\n  const hasBoolUnit = isBooleanUnit(unit);\n  const isNumType = field.type === FieldType.number;\n  const isLocaleFormat = unit === 'locale';\n  const canTrimTrailingDecimalZeros =\n    !hasDateUnit && !hasCurrencyUnit && !hasBoolUnit && !isLocaleFormat && isNumType && config.decimals == null;\n\n  const formatFunc = getValueFormat(unit || 'none');\n  const scaleFunc = getScaleCalculator(field, options.theme);\n\n  return (value: unknown, adjacentDecimals?: DecimalCount) => {\n    const { mappings } = config;\n    const isStringUnit = unit === 'string';\n\n    if (hasDateUnit && typeof value === 'string') {\n      value = toUtc(value).valueOf();\n    }\n\n    let numeric = isStringUnit ? NaN : anyToNumber(value);\n    let text: string | undefined;\n    let prefix: string | undefined;\n    let suffix: string | undefined;\n    let color: string | undefined;\n    let icon: string | undefined;\n    let percent: number | undefined;\n\n    if (mappings && mappings.length > 0) {\n      const mappingResult = getValueMappingResult(mappings, value);\n\n      if (mappingResult) {\n        if (mappingResult.text != null) {\n          text = mappingResult.text;\n        }\n\n        if (mappingResult.color != null) {\n          color = options.theme.visualization.getColorByName(mappingResult.color);\n        }\n\n        if (mappingResult.icon != null) {\n          icon = mappingResult.icon;\n        }\n      }\n    } else if (field.type === FieldType.enum) {\n      // Apply enum display handling if field is enum type and no mappings are specified\n      if (value == null) {\n        return {\n          text: '',\n          numeric: NaN,\n        };\n      }\n\n      const enumIndex = +value;\n      if (config && config.type && config.type.enum) {\n        const { text: enumText, color: enumColor } = config.type.enum;\n\n        text = enumText ? enumText[enumIndex] : `${value}`;\n        // If no color specified in enum field config we will fallback to iterating through the theme palette\n        color = enumColor ? enumColor[enumIndex] : undefined;\n\n        if (color == null) {\n          const namedColor = palette[enumIndex % palette.length];\n          color = options.theme.visualization.getColorByName(namedColor);\n        }\n      }\n    }\n\n    if (!Number.isNaN(numeric)) {\n      if (text == null && !isBoolean(value)) {\n        let v: FormattedValue;\n\n        if (canTrimTrailingDecimalZeros && adjacentDecimals != null) {\n          v = formatFunc(numeric, adjacentDecimals, null, options.timeZone, showMs);\n\n          // if no explicit decimals config, we strip trailing zeros e.g. 60.00 -> 60\n          // this is needed because we may have determined the minimum determined `adjacentDecimals` for y tick increments based on\n          // e.g. 'seconds' field unit (0.15s, 0.20s, 0.25s), but then formatFunc decided to return milli or nanos (150, 200, 250)\n          // so we end up with excess precision: 150.00, 200.00, 250.00\n          // #73795 - some units, like duration formats, return text which cannot be coerced back into a number safely at this point.\n          const asNum = +v.text;\n          if (!Number.isNaN(asNum)) {\n            v.text = asNum + '';\n          }\n        } else {\n          v = formatFunc(numeric, config.decimals, null, options.timeZone, showMs);\n        }\n\n        text = v.text;\n        suffix = v.suffix;\n        prefix = v.prefix;\n      }\n\n      // Return the value along with scale info\n      if (color == null) {\n        const scaleResult = scaleFunc(numeric);\n        color = scaleResult.color;\n        percent = scaleResult.percent;\n      }\n    }\n\n    if (text == null && isArray(value)) {\n      text = join(value, ', ');\n    }\n\n    if (text == null) {\n      text = toString(value);\n      if (!text) {\n        if (config.noValue) {\n          text = config.noValue;\n        } else {\n          text = ''; // No data?\n        }\n      }\n    }\n\n    if (!color) {\n      const scaleResult = scaleFunc(-Infinity);\n      color = scaleResult.color;\n      percent = scaleResult.percent;\n    }\n\n    const display: DisplayValue = {\n      text,\n      numeric,\n      prefix,\n      suffix,\n      color,\n    };\n\n    if (icon != null) {\n      display.icon = icon;\n    }\n\n    if (percent != null) {\n      display.percent = percent;\n    }\n\n    return display;\n  };\n}\n\nfunction toStringProcessor(value: unknown): DisplayValue {\n  return { text: toString(value), numeric: anyToNumber(value) };\n}\n\nexport function getRawDisplayProcessor(): DisplayProcessor {\n  return (value: unknown) => ({\n    text: getFieldTypeFromValue(value) === 'other' ? `${JSON.stringify(value, getCircularReplacer())}` : `${value}`,\n    numeric: null as unknown as number,\n  });\n}\n\nconst getCircularReplacer = () => {\n  const seen = new WeakSet();\n  return (_key: string, value: object | null) => {\n    if (typeof value === 'object' && value !== null) {\n      if (seen.has(value)) {\n        return;\n      }\n      seen.add(value);\n    }\n    return value;\n  };\n};\n"],"names":["isEmpty","FieldType","dateTimeParse","isBooleanUnit","getValueFormat","getScaleCalculator","toUtc","anyToNumber","getValueMappingResult","isBoolean","isArray","join","toString","getFieldTypeFromValue"],"mappings":"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"}