UNPKG

@grafana/ui

Version:
1 lines 15.4 kB
{"version":3,"file":"UPlotScaleBuilder.mjs","sources":["../../../../../src/components/uPlot/config/UPlotScaleBuilder.ts"],"sourcesContent":["import uPlot, { Scale, Range } from 'uplot';\n\nimport { DecimalCount, incrRoundDn, incrRoundUp, isBooleanUnit } from '@grafana/data';\nimport { ScaleOrientation, ScaleDirection, ScaleDistribution, StackingMode } from '@grafana/schema';\n\nimport { PlotConfigBuilder } from '../types';\n\nexport interface ScaleProps {\n scaleKey: string;\n isTime?: boolean;\n auto?: boolean;\n min?: number | null;\n max?: number | null;\n softMin?: number | null;\n softMax?: number | null;\n range?: Scale.Range;\n distribution?: ScaleDistribution;\n orientation: ScaleOrientation;\n direction: ScaleDirection;\n log?: number;\n linearThreshold?: number;\n centeredZero?: boolean;\n decimals?: DecimalCount;\n stackingMode?: StackingMode;\n padMinBy?: number;\n padMaxBy?: number;\n}\n\nexport class UPlotScaleBuilder extends PlotConfigBuilder<ScaleProps, Scale> {\n merge(props: ScaleProps) {\n this.props.min = optMinMax('min', this.props.min, props.min);\n this.props.max = optMinMax('max', this.props.max, props.max);\n }\n\n getConfig(): Scale {\n let {\n isTime,\n auto,\n scaleKey,\n min: hardMin,\n max: hardMax,\n softMin,\n softMax,\n range,\n direction,\n orientation,\n centeredZero,\n decimals,\n stackingMode,\n padMinBy = 0.1,\n padMaxBy = 0.1,\n } = this.props;\n\n if (stackingMode === StackingMode.Percent) {\n if (hardMin == null && softMin == null) {\n softMin = 0;\n }\n\n if (hardMax == null && softMax == null) {\n softMax = 1;\n }\n }\n\n const distr = this.props.distribution;\n\n const distribution = !isTime\n ? {\n distr:\n distr === ScaleDistribution.Symlog\n ? 4\n : distr === ScaleDistribution.Log\n ? 3\n : distr === ScaleDistribution.Ordinal\n ? 2\n : 1,\n log:\n distr === ScaleDistribution.Log || distr === ScaleDistribution.Symlog ? (this.props.log ?? 2) : undefined,\n asinh: distr === ScaleDistribution.Symlog ? (this.props.linearThreshold ?? 1) : undefined,\n }\n : {};\n\n // guard against invalid log scale limits <= 0, or snap to log boundaries\n if (distr === ScaleDistribution.Log) {\n let logBase = this.props.log!;\n let logFn = logBase === 2 ? Math.log2 : Math.log10;\n\n if (hardMin != null) {\n if (hardMin <= 0) {\n hardMin = null;\n } else {\n hardMin = logBase ** Math.floor(logFn(hardMin));\n }\n }\n\n if (hardMax != null) {\n if (hardMax <= 0) {\n hardMax = null;\n } else {\n hardMax = logBase ** Math.ceil(logFn(hardMax));\n }\n }\n\n if (softMin != null) {\n if (softMin <= 0) {\n softMin = null;\n } else {\n softMin = logBase ** Math.floor(logFn(softMin));\n }\n }\n\n if (softMax != null) {\n if (softMax <= 0) {\n softMax = null;\n } else {\n softMax = logBase ** Math.ceil(logFn(softMax));\n }\n }\n }\n /*\n // snap to symlog boundaries\n else if (distr === ScaleDistribution.Symlog) {\n let logBase = this.props.log!;\n let logFn = logBase === 2 ? Math.log2 : Math.log10;\n\n let sign = Math.sign(hardMin);\n\n if (hardMin != null) {\n hardMin = logBase ** Math.floor(logFn(hardMin));\n }\n\n if (hardMax != null) {\n hardMax = logBase ** Math.ceil(logFn(hardMax));\n }\n\n if (softMin != null) {\n softMin = logBase ** Math.floor(logFn(softMin));\n }\n\n if (softMax != null) {\n softMax = logBase ** Math.ceil(logFn(softMax));\n }\n }\n */\n\n // uPlot's default ranging config for both min & max is {pad: 0.1, hard: null, soft: 0, mode: 3}\n let softMinMode: Range.SoftMode = softMin == null ? 3 : 1;\n let softMaxMode: Range.SoftMode = softMax == null ? 3 : 1;\n\n const rangeConfig: Range.Config = {\n min: {\n pad: padMinBy,\n hard: hardMin ?? -Infinity,\n soft: softMin || 0,\n mode: softMinMode,\n },\n max: {\n pad: padMaxBy,\n hard: hardMax ?? Infinity,\n soft: softMax || 0,\n mode: softMaxMode,\n },\n };\n\n let hardMinOnly = softMin == null && hardMin != null;\n let hardMaxOnly = softMax == null && hardMax != null;\n let hasFixedRange = hardMinOnly && hardMaxOnly;\n\n const rangeFn: uPlot.Range.Function = (\n u: uPlot,\n dataMin: number | null,\n dataMax: number | null,\n scaleKey: string\n ) => {\n const scale = u.scales[scaleKey];\n\n let minMax: uPlot.Range.MinMax = [dataMin, dataMax];\n\n // happens when all series on a scale are `show: false`, re-returning nulls will auto-disable axis\n if (!hasFixedRange && dataMin == null && dataMax == null) {\n return minMax;\n }\n\n let logBase = scale.log ?? 10;\n\n if (scale.distr === 1 || scale.distr === 2 || scale.distr === 4) {\n if (centeredZero) {\n let absMin = Math.abs(dataMin!);\n let absMax = Math.abs(dataMax!);\n let max = Math.max(absMin, absMax);\n\n // flat 0\n if (max === 0) {\n max = 80;\n }\n\n dataMin = -max;\n dataMax = max;\n }\n\n if (scale.distr === 4) {\n minMax = uPlot.rangeAsinh(dataMin!, dataMax!, logBase, true);\n } else {\n // @ts-ignore here we may use hardMin / hardMax to make sure any extra padding is computed from a more accurate delta\n minMax = uPlot.rangeNum(hardMinOnly ? hardMin : dataMin, hardMaxOnly ? hardMax : dataMax, rangeConfig);\n }\n } else if (scale.distr === 3) {\n minMax = uPlot.rangeLog(hardMin ?? dataMin!, hardMax ?? dataMax!, logBase, true);\n }\n\n if (decimals === 0) {\n if (scale.distr === 1 || scale.distr === 2) {\n minMax[0] = incrRoundDn(minMax[0]!, 1);\n minMax[1] = incrRoundUp(minMax[1]!, 1);\n }\n // log2 or log10 scale min must be clamped to 1\n else if (scale.distr === 3) {\n let logFn = scale.log === 2 ? Math.log2 : Math.log10;\n\n if (minMax[0]! <= 1) {\n // clamp min\n minMax[0] = 1;\n } else {\n // snap min to nearest mag below\n let minExp = Math.floor(logFn(minMax[0]!));\n minMax[0] = logBase ** minExp;\n }\n\n // snap max to nearest mag above\n let maxExp = Math.ceil(logFn(minMax[1]!));\n minMax[1] = logBase ** maxExp;\n\n // inflate max by mag if same\n if (minMax[0] === minMax[1]) {\n minMax[1] *= logBase;\n }\n }\n // TODO: this should be better. symlog values can be <= 0, but should also be snapped to log2 or log10 boundaries\n // for now we just do same as linear snapping above, so may have non-neat range and ticks at edges\n else if (scale.distr === 4) {\n minMax[0] = incrRoundDn(minMax[0]!, 1);\n minMax[1] = incrRoundUp(minMax[1]!, 1);\n }\n }\n\n if (scale.distr === 1 || scale.distr === 4) {\n // if all we got were hard limits, treat them as static min/max\n if (hardMinOnly) {\n minMax[0] = hardMin!;\n }\n\n if (hardMaxOnly) {\n minMax[1] = hardMax!;\n }\n }\n\n // guard against invalid y ranges\n if (minMax[0]! >= minMax[1]!) {\n minMax[0] = scale.distr === 3 ? 1 : 0;\n minMax[1] = 100;\n }\n\n return minMax;\n };\n\n auto ??= !isTime && !hasFixedRange;\n\n if (isBooleanUnit(scaleKey)) {\n auto = false;\n range = [0, 1];\n }\n\n return {\n [scaleKey]: {\n time: isTime,\n auto,\n range: range ?? rangeFn,\n dir: direction,\n ori: orientation,\n ...distribution,\n },\n };\n }\n}\n\nexport function optMinMax(minmax: 'min' | 'max', a?: number | null, b?: number | null): undefined | number | null {\n const hasA = !(a === undefined || a === null);\n const hasB = !(b === undefined || b === null);\n if (hasA) {\n if (!hasB) {\n return a;\n }\n if (minmax === 'min') {\n return a! < b! ? a : b;\n }\n return a! > b! ? a : b;\n }\n return b;\n}\n"],"names":["scaleKey","_a"],"mappings":"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"}