@grafana/ui
Version:
Grafana Components Library
515 lines (512 loc) • 17.9 kB
JavaScript
import { clone, sampleSize } from 'lodash';
import memoize from 'micro-memoize';
import tinycolor from 'tinycolor2';
import { FieldType, getFieldDisplayName, isDataFrame, isTimeSeriesFrame, formattedValueToString, isDataFrameWithValue, fieldReducers, getDisplayProcessor, reduceField } from '@grafana/data';
import { TableCellDisplayMode, TableCellBackgroundDisplayMode, BarGaugeDisplayMode } from '@grafana/schema';
import { getTextColorForAlphaBackground } from '../../utils/colors.mjs';
import { ActionsCell } from './ActionsCell.mjs';
import { BarGaugeCell } from './Cells/BarGaugeCell.mjs';
import { DataLinksCell } from './Cells/DataLinksCell.mjs';
import { DefaultCell } from './Cells/DefaultCell.mjs';
import { GeoCell } from './Cells/GeoCell.mjs';
import { ImageCell } from './Cells/ImageCell.mjs';
import { JSONViewCell } from './Cells/JSONViewCell.mjs';
import { SparklineCell } from './Cells/SparklineCell.mjs';
import { getFooterValue } from './TableRT/FooterRow.mjs';
import { RowExpander } from './TableRT/RowExpander.mjs';
const EXPANDER_WIDTH = 50;
function getTextAlign(field) {
if (!field) {
return "flex-start";
}
if (field.config.custom) {
const custom = field.config.custom;
switch (custom.align) {
case "right":
return "flex-end";
case "left":
return "flex-start";
case "center":
return "center";
}
}
if (field.type === FieldType.number) {
return "flex-end";
}
return "flex-start";
}
function getColumns(data, availableWidth, columnMinWidth, expander, footerValues, isCountRowsSet) {
var _a, _b;
const columns = [];
let fieldCountWithoutWidth = 0;
if (expander) {
columns.push({
// Make an expander cell
Header: () => null,
// No header
id: "expander",
// It needs an ID
// @ts-expect-error
// TODO fix type error here
Cell: RowExpander,
width: EXPANDER_WIDTH,
minWidth: EXPANDER_WIDTH,
filter: (_rows, _id, _filterValues) => {
return [];
},
justifyContent: "left",
field: data.fields[0],
sortType: "basic"
});
availableWidth -= EXPANDER_WIDTH;
}
for (const [fieldIndex, field] of data.fields.entries()) {
const fieldTableOptions = field.config.custom || {};
if (fieldTableOptions.hidden || field.type === FieldType.nestedFrames) {
continue;
}
if (fieldTableOptions.width) {
availableWidth -= fieldTableOptions.width;
} else {
fieldCountWithoutWidth++;
}
const selectSortType = (type) => {
switch (type) {
case FieldType.number:
case FieldType.frame:
return "number";
case FieldType.time:
return "basic";
default:
return "alphanumeric-insensitive";
}
};
const Cell = getCellComponent((_a = fieldTableOptions.cellOptions) == null ? void 0 : _a.type, field);
columns.push({
// @ts-expect-error
// TODO fix type error here
Cell,
id: fieldIndex.toString(),
field,
Header: fieldTableOptions.hideHeader ? "" : getFieldDisplayName(field, data),
accessor: (_row, i) => field.values[i],
sortType: selectSortType(field.type),
width: fieldTableOptions.width,
minWidth: (_b = fieldTableOptions.minWidth) != null ? _b : columnMinWidth,
filter: memoize(filterByValue(field)),
justifyContent: getTextAlign(field),
Footer: getFooterValue(fieldIndex, footerValues, isCountRowsSet)
});
}
let sharedWidth = availableWidth / fieldCountWithoutWidth;
for (let i = fieldCountWithoutWidth; i > 0; i--) {
for (const column of columns) {
if (!column.width && column.minWidth > sharedWidth) {
column.width = column.minWidth;
availableWidth -= column.width;
fieldCountWithoutWidth -= 1;
sharedWidth = availableWidth / fieldCountWithoutWidth;
}
}
}
for (const column of columns) {
if (!column.width) {
column.width = sharedWidth;
}
column.minWidth = 50;
}
return columns;
}
function getCellComponent(displayMode, field) {
switch (displayMode) {
case TableCellDisplayMode.Custom:
case TableCellDisplayMode.ColorText:
case TableCellDisplayMode.ColorBackground:
return DefaultCell;
case TableCellDisplayMode.Image:
return ImageCell;
case TableCellDisplayMode.Gauge:
return BarGaugeCell;
case TableCellDisplayMode.Sparkline:
return SparklineCell;
case TableCellDisplayMode.JSONView:
return JSONViewCell;
case TableCellDisplayMode.DataLinks:
return DataLinksCell;
case TableCellDisplayMode.Actions:
return ActionsCell;
case TableCellDisplayMode.Pill:
return DefaultCell;
}
if (field.type === FieldType.geo) {
return GeoCell;
}
if (field.type === FieldType.frame) {
const firstValue = field.values[0];
if (isDataFrame(firstValue) && isTimeSeriesFrame(firstValue)) {
return SparklineCell;
}
return JSONViewCell;
}
if (field.type === FieldType.other) {
return JSONViewCell;
}
return DefaultCell;
}
function filterByValue(field) {
return function(rows, id, filterValues) {
if (rows.length === 0) {
return rows;
}
if (!filterValues) {
return rows;
}
if (!field) {
return rows;
}
return rows.filter((row) => {
if (!row.values.hasOwnProperty(id)) {
return false;
}
const value = rowToFieldValue(row, field);
return filterValues.find((filter) => filter.value === value) !== void 0;
});
};
}
function calculateUniqueFieldValues(rows, field) {
if (!field || rows.length === 0) {
return {};
}
const set = {};
for (let index = 0; index < rows.length; index++) {
const value = rowToFieldValue(rows[index], field);
set[value || "(Blanks)"] = value;
}
return set;
}
function rowToFieldValue(row, field) {
if (!field || !row) {
return "";
}
const fieldValue = field.values[row.index];
const displayValue = field.display ? field.display(fieldValue) : fieldValue;
const value = field.display ? formattedValueToString(displayValue) : displayValue;
return value;
}
function valuesToOptions(unique) {
return Object.keys(unique).reduce((all, key) => all.concat({ value: unique[key], label: key }), []).sort(sortOptions);
}
function sortOptions(a, b) {
if (a.label === void 0 && b.label === void 0) {
return 0;
}
if (a.label === void 0 && b.label !== void 0) {
return -1;
}
if (a.label !== void 0 && b.label === void 0) {
return 1;
}
if (a.label < b.label) {
return -1;
}
if (a.label > b.label) {
return 1;
}
return 0;
}
function getFilteredOptions(options, filterValues) {
if (!filterValues) {
return [];
}
return options.filter((option) => filterValues.some((filtered) => filtered.value === option.value));
}
function sortCaseInsensitive(a, b, id) {
return String(a.values[id]).localeCompare(String(b.values[id]), void 0, { sensitivity: "base" });
}
function sortNumber(rowA, rowB, id) {
const a = toNumber(rowA.values[id]);
const b = toNumber(rowB.values[id]);
return a === b ? 0 : a > b ? 1 : -1;
}
function toNumber(value) {
var _a;
if (isDataFrameWithValue(value)) {
return (_a = value.value) != null ? _a : Number.NEGATIVE_INFINITY;
}
if (value === null || value === void 0 || value === "" || isNaN(value)) {
return Number.NEGATIVE_INFINITY;
}
if (typeof value === "number") {
return value;
}
return Number(value);
}
function getFooterItems(filterFields, values, options, theme2) {
addMissingColumnIndex(filterFields);
return filterFields.map((data, i) => {
var _a;
if (((_a = data == null ? void 0 : data.field) == null ? void 0 : _a.type) !== FieldType.number) {
if (i === 0 && options.reducer && options.reducer.length > 0) {
const reducer = fieldReducers.get(options.reducer[0]);
return reducer.name;
}
return void 0;
}
let newField = clone(data.field);
newField.values = values[data.id];
newField.state = void 0;
data.field = newField;
if (options.fields && options.fields.length > 0) {
const f = options.fields.find((f2) => {
var _a2;
return f2 === ((_a2 = data == null ? void 0 : data.field) == null ? void 0 : _a2.name);
});
if (f) {
return getFormattedValue(data.field, options.reducer, theme2);
}
return void 0;
}
return getFormattedValue(data.field, options.reducer || [], theme2);
});
}
function getFormattedValue(field, reducer, theme) {
var _a;
const calc = reducer[0];
if (calc === void 0) {
return "";
}
const format = (_a = field.display) != null ? _a : getDisplayProcessor({ field, theme });
const fieldCalcValue = reduceField({ field, reducers: reducer })[calc];
const reducerInfo = fieldReducers.get(calc);
if (reducerInfo.preservesUnits) {
return formattedValueToString(format(fieldCalcValue));
}
return formattedValueToString({ text: fieldCalcValue });
}
function createFooterCalculationValues(rows) {
const values = [];
for (const key in rows) {
for (const [valKey, val] of Object.entries(rows[key].values)) {
if (values[valKey] === void 0) {
values[valKey] = [];
}
values[valKey].push(val);
}
}
return values;
}
const defaultCellOptions = { type: TableCellDisplayMode.Auto };
function getCellOptions(field) {
var _a, _b, _c;
if ((_a = field.config.custom) == null ? void 0 : _a.displayMode) {
return migrateTableDisplayModeToCellOptions((_b = field.config.custom) == null ? void 0 : _b.displayMode);
}
if (!((_c = field.config.custom) == null ? void 0 : _c.cellOptions)) {
return defaultCellOptions;
}
return field.config.custom.cellOptions;
}
function migrateTableDisplayModeToCellOptions(displayMode) {
switch (displayMode) {
// In the case of the gauge we move to a different option
case "basic":
case "gradient-gauge":
case "lcd-gauge":
let gaugeMode = BarGaugeDisplayMode.Basic;
if (displayMode === "gradient-gauge") {
gaugeMode = BarGaugeDisplayMode.Gradient;
} else if (displayMode === "lcd-gauge") {
gaugeMode = BarGaugeDisplayMode.Lcd;
}
return {
type: TableCellDisplayMode.Gauge,
mode: gaugeMode
};
// Also true in the case of the color background
case "color-background":
case "color-background-solid":
let mode = TableCellBackgroundDisplayMode.Basic;
if (displayMode === "color-background") {
mode = TableCellBackgroundDisplayMode.Gradient;
}
return {
type: TableCellDisplayMode.ColorBackground,
mode
};
default:
return {
// @ts-ignore
type: displayMode
};
}
}
function addMissingColumnIndex(columns) {
var _a;
const missingIndex = columns.findIndex((field, index) => (field == null ? void 0 : field.id) !== String(index));
if (missingIndex === -1 || ((_a = columns[missingIndex]) == null ? void 0 : _a.id) === "expander") {
return;
}
columns.splice(missingIndex, 0, { id: String(missingIndex) });
addMissingColumnIndex(columns);
}
function getAlignmentFactor(field, displayValue, rowIndex) {
var _a;
let alignmentFactor = (_a = field.state) == null ? void 0 : _a.alignmentFactors;
if (alignmentFactor) {
if (formattedValueToString(alignmentFactor).length < formattedValueToString(displayValue).length) {
alignmentFactor = { ...displayValue };
field.state.alignmentFactors = alignmentFactor;
}
return alignmentFactor;
} else {
alignmentFactor = { ...displayValue };
const maxIndex = Math.min(field.values.length, rowIndex + 1e3);
for (let i = rowIndex + 1; i < maxIndex; i++) {
const nextDisplayValue = field.display(field.values[i]);
if (formattedValueToString(alignmentFactor).length > formattedValueToString(nextDisplayValue).length) {
alignmentFactor.text = displayValue.text;
}
}
if (field.state) {
field.state.alignmentFactors = alignmentFactor;
} else {
field.state = { alignmentFactors: alignmentFactor };
}
return alignmentFactor;
}
}
function isPointTimeValAroundTableTimeVal(pointTime, rowTime, threshold) {
return Math.abs(Math.floor(pointTime) - rowTime) < threshold;
}
function calculateAroundPointThreshold(timeField) {
let max = -Number.MAX_VALUE;
let min = Number.MAX_VALUE;
if (timeField.values.length < 2) {
return 0;
}
for (let i = 0; i < timeField.values.length; i++) {
const value = timeField.values[i];
if (value > max) {
max = value;
}
if (value < min) {
min = value;
}
}
return (max - min) / timeField.values.length;
}
function getCellColors(theme, cellOptions, displayValue) {
var _a;
const darkeningFactor = theme.isDark ? 1 : -0.7;
let textColor = void 0;
let bgColor = void 0;
let bgHoverColor = void 0;
if (cellOptions.type === TableCellDisplayMode.ColorText) {
textColor = displayValue.color;
} else if (cellOptions.type === TableCellDisplayMode.ColorBackground) {
const mode = (_a = cellOptions.mode) != null ? _a : TableCellBackgroundDisplayMode.Gradient;
if (mode === TableCellBackgroundDisplayMode.Basic) {
textColor = getTextColorForAlphaBackground(displayValue.color, theme.isDark);
bgColor = tinycolor(displayValue.color).toRgbString();
bgHoverColor = tinycolor(displayValue.color).setAlpha(1).toRgbString();
} else if (mode === TableCellBackgroundDisplayMode.Gradient) {
const hoverColor = tinycolor(displayValue.color).setAlpha(1).toRgbString();
const bgColor2 = tinycolor(displayValue.color).darken(10 * darkeningFactor).spin(5);
textColor = getTextColorForAlphaBackground(displayValue.color, theme.isDark);
bgColor = `linear-gradient(120deg, ${bgColor2.toRgbString()}, ${displayValue.color})`;
bgHoverColor = `linear-gradient(120deg, ${bgColor2.setAlpha(1).toRgbString()}, ${hoverColor})`;
}
}
return { textColor, bgColor, bgHoverColor };
}
function guessTextBoundingBox(text, headerGroup, osContext, lineHeight, defaultRowHeight, padding = 0) {
var _a;
const width = Number((_a = headerGroup == null ? void 0 : headerGroup.width) != null ? _a : 300);
const LINE_SCALE_FACTOR = 1.17;
const LOW_LINE_PAD = 42;
const PADDING = padding * 2;
if (osContext !== null && typeof text === "string") {
const words = text.split(/\s/);
const lines = [];
let currentLine = "";
let extraLines = 0;
for (let i = 0; i < words.length; i++) {
const currentWord = words[i];
let lineWidth = osContext.measureText(currentLine + " " + currentWord).width;
if (lineWidth < width - PADDING) {
currentLine += " " + currentWord;
} else {
lines.push({
width: lineWidth,
line: currentLine
});
currentLine = currentWord;
}
}
for (let i = 0; i < lines.length; i++) {
if (lines[i].width > width) {
let extra = Math.floor(lines[i].width / width) - 1;
extraLines += extra;
}
}
let lineNumber = lines.length + extraLines;
let height = 38;
if (lineNumber > 5) {
height = lineNumber * lineHeight * LINE_SCALE_FACTOR;
} else {
height = lineNumber * lineHeight + LOW_LINE_PAD;
}
height += PADDING;
return { width, height };
}
return { width, height: defaultRowHeight };
}
function guessLongestField(fieldConfig, data) {
var _a, _b, _c, _d, _e, _f, _g;
let longestField = void 0;
const SAMPLE_SIZE = 3;
if ((_b = (_a = fieldConfig.defaults.custom) == null ? void 0 : _a.cellOptions) == null ? void 0 : _b.wrapText) {
const stringFields = data.fields.filter((field) => field.type === FieldType.string);
if (stringFields.length >= 1 && stringFields[0].values.length > 0) {
const numValues = stringFields[0].values.length;
let longestLength = 0;
if (numValues <= 30) {
for (const field of stringFields) {
const fieldLength = (_d = (_c = field.values.find((v) => v != null)) == null ? void 0 : _c.length) != null ? _d : 0;
if (fieldLength > longestLength) {
longestLength = fieldLength;
longestField = field;
}
}
} else {
for (const field of stringFields) {
const vals = sampleSize(field.values, SAMPLE_SIZE);
const meanLength = (((_e = vals[0]) == null ? void 0 : _e.length) + ((_f = vals[1]) == null ? void 0 : _f.length) + ((_g = vals[2]) == null ? void 0 : _g.length)) / 3;
if (meanLength > longestLength) {
longestLength = meanLength;
longestField = field;
}
}
}
}
}
return longestField;
}
const getDataLinksActionsTooltipUtils = (links, actions) => {
const hasMultipleLinksOrActions = links.length > 1 || Boolean(void 0 );
const shouldShowLink = links.length === 1 && !Boolean(void 0 );
return { shouldShowLink, hasMultipleLinksOrActions };
};
const shouldTriggerTooltip = (event) => {
return event.target === event.currentTarget;
};
const tooltipOnClickHandler = (setTooltipCoords) => {
return (event) => {
if (shouldTriggerTooltip(event)) {
const { clientX, clientY } = event;
setTooltipCoords({ clientX, clientY });
}
};
};
export { EXPANDER_WIDTH, calculateAroundPointThreshold, calculateUniqueFieldValues, createFooterCalculationValues, filterByValue, getAlignmentFactor, getCellColors, getCellComponent, getCellOptions, getColumns, getDataLinksActionsTooltipUtils, getFilteredOptions, getFooterItems, getTextAlign, guessLongestField, guessTextBoundingBox, isPointTimeValAroundTableTimeVal, migrateTableDisplayModeToCellOptions, rowToFieldValue, sortCaseInsensitive, sortNumber, sortOptions, tooltipOnClickHandler, valuesToOptions };
//# sourceMappingURL=utils.mjs.map