@grafana/ui
Version:
Grafana Components Library
390 lines (387 loc) • 13.1 kB
JavaScript
import { clone, sampleSize } from 'lodash';
import memoize from 'micro-memoize';
import { FieldType, getFieldDisplayName, isDataFrame, isTimeSeriesFrame, formattedValueToString, isDataFrameWithValue, fieldReducers, getDisplayProcessor, reduceField } from '@grafana/data';
import { TableCellDisplayMode } from '@grafana/schema';
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';
import { getTextAlign } from './cellUtils.mjs';
"use strict";
const EXPANDER_WIDTH = 50;
function getColumns(data, availableWidth, columnMinWidth, expander, footerValues, isCountRowsSet) {
var _a, _b, _c;
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 (
// @ts-ignore this was the former hidden option; we support it for legacy use cases while TableRT is being sunset.
fieldTableOptions.hidden || ((_a = fieldTableOptions.hideFrom) == null ? void 0 : _a.viz) || 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((_b = fieldTableOptions.cellOptions) == null ? void 0 : _b.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: (_c = fieldTableOptions.minWidth) != null ? _c : 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;
}
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 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 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 wordCount = 0;
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;
wordCount++;
} else {
lines.push({
width: lineWidth,
line: currentLine
});
currentLine = currentWord;
wordCount = 0;
}
}
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;
}
export { EXPANDER_WIDTH, calculateAroundPointThreshold, calculateUniqueFieldValues, createFooterCalculationValues, filterByValue, getColumns, getFilteredOptions, getFooterItems, guessLongestField, guessTextBoundingBox, isPointTimeValAroundTableTimeVal, rowToFieldValue, sortCaseInsensitive, sortNumber, sortOptions, valuesToOptions };
//# sourceMappingURL=utils.mjs.map