@grafana/ui
Version:
Grafana Components Library
362 lines (359 loc) • 13.9 kB
JavaScript
import tinycolor from 'tinycolor2';
import { FieldType, formattedValueToString } from '@grafana/data';
import { TableCellDisplayMode, BarGaugeDisplayMode, TableCellBackgroundDisplayMode, TableCellHeight } from '@grafana/schema';
import { getTextColorForAlphaBackground } from '../../../utils/colors.mjs';
import { TABLE, COLUMN } from './constants.mjs';
function getDefaultRowHeight(theme, cellHeight) {
const bodyFontSize = theme.typography.fontSize;
const lineHeight = theme.typography.body.lineHeight;
switch (cellHeight) {
case TableCellHeight.Sm:
return 36;
case TableCellHeight.Md:
return 42;
case TableCellHeight.Lg:
return TABLE.MAX_CELL_HEIGHT;
}
return TABLE.CELL_PADDING * 2 + bodyFontSize * lineHeight;
}
function isCellInspectEnabled(field) {
var _a, _b, _c;
return (_c = (_b = (_a = field.config) == null ? void 0 : _a.custom) == null ? void 0 : _b.inspect) != null ? _c : false;
}
function shouldTextWrap(field) {
const cellOptions = getCellOptions(field);
return Boolean(cellOptions == null ? void 0 : cellOptions.wrapText);
}
const spaceRegex = /[\s-]/;
function getMaxWrapCell(fields, rowIdx, { colWidths, avgCharWidth, wrappedColIdxs }) {
let maxLines = 1;
let maxLinesIdx = -1;
let maxLinesText = "";
for (let i = 0; i < colWidths.length; i++) {
if (wrappedColIdxs[i]) {
const field = fields[i];
const cellTextRaw = rowIdx === -1 ? getDisplayName(field) : field.values[rowIdx];
if (cellTextRaw != null) {
const cellText = String(cellTextRaw);
if (spaceRegex.test(cellText)) {
const charsPerLine = colWidths[i] / avgCharWidth;
const approxLines = cellText.length / charsPerLine;
if (approxLines > maxLines) {
maxLines = approxLines;
maxLinesIdx = i;
maxLinesText = cellText;
}
}
}
}
}
return { text: maxLinesText, idx: maxLinesIdx, numLines: maxLines };
}
function shouldTextOverflow(field) {
return field.type === FieldType.string && // Tech debt: Technically image cells are of type string, which is misleading (kinda?)
// so we need to ensure we don't apply overflow hover states for type image
getCellOptions(field).type !== TableCellDisplayMode.Image && !shouldTextWrap(field) && !isCellInspectEnabled(field);
}
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";
}
const DEFAULT_CELL_OPTIONS = { type: TableCellDisplayMode.Auto };
function getCellOptions(field) {
var _a, _b, _c, _d;
if ((_a = field.config.custom) == null ? void 0 : _a.displayMode) {
return migrateTableDisplayModeToCellOptions((_b = field.config.custom) == null ? void 0 : _b.displayMode);
}
return (_d = (_c = field.config.custom) == null ? void 0 : _c.cellOptions) != null ? _d : DEFAULT_CELL_OPTIONS;
}
function getAlignmentFactor(field, displayValue, rowIndex) {
var _a, _b, _c;
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 = (_c = (_b = field.display) == null ? void 0 : _b.call(field, field.values[i])) != null ? _c : 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;
}
}
const CELL_COLOR_DARKENING_MULTIPLIER = 10;
const CELL_GRADIENT_DARKENING_MULTIPLIER = 15;
const CELL_GRADIENT_HUE_ROTATION_DEGREES = 5;
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).darken(CELL_COLOR_DARKENING_MULTIPLIER * darkeningFactor).toRgbString();
} else if (mode === TableCellBackgroundDisplayMode.Gradient) {
const hoverColor = tinycolor(displayValue.color).darken(CELL_GRADIENT_DARKENING_MULTIPLIER * darkeningFactor).toRgbString();
const bgColor2 = tinycolor(displayValue.color).darken(CELL_COLOR_DARKENING_MULTIPLIER * darkeningFactor).spin(CELL_GRADIENT_HUE_ROTATION_DEGREES);
textColor = getTextColorForAlphaBackground(displayValue.color, theme.isDark);
bgColor = `linear-gradient(120deg, ${bgColor2.toRgbString()}, ${displayValue.color})`;
bgHoverColor = `linear-gradient(120deg, ${bgColor2.toRgbString()}, ${hoverColor})`;
}
}
return { textColor, bgColor, bgHoverColor };
}
const extractPixelValue = (spacing) => {
return typeof spacing === "number" ? spacing : parseFloat(spacing) || 0;
};
const getCellLinks = (field, rowIdx) => {
let links;
if (field.getLinks) {
links = field.getLinks({
valueRowIndex: rowIdx
});
}
if (!links) {
return;
}
for (let i = 0; i < (links == null ? void 0 : links.length); i++) {
if (links[i].onClick) {
const origOnClick = links[i].onClick;
links[i].onClick = (event) => {
if (!(event.ctrlKey || event.metaKey || event.shiftKey)) {
event.preventDefault();
origOnClick(event, {
field,
rowIndex: rowIdx
});
}
};
}
}
return links.filter((link) => link.href || link.onClick != null);
};
function applySort(rows, fields, sortColumns, columnTypes = getColumnTypes(fields), hasNestedFrames = getIsNestedTable(fields)) {
if (sortColumns.length === 0) {
return rows;
}
const compareRows = (a, b) => {
let result = 0;
for (let i = 0; i < sortColumns.length; i++) {
const { columnKey, direction } = sortColumns[i];
const compare2 = getComparator(columnTypes[columnKey]);
const sortDir = direction === "ASC" ? 1 : -1;
result = sortDir * compare2(a[columnKey], b[columnKey]);
if (result !== 0) {
break;
}
}
return result;
};
if (hasNestedFrames) {
return processNestedTableRows(rows, (parents) => [...parents].sort(compareRows));
}
return [...rows].sort(compareRows);
}
const frameToRecords = (frame) => {
const fnBody = `
const rows = Array(frame.length);
const values = frame.fields.map(f => f.values);
let rowCount = 0;
for (let i = 0; i < frame.length; i++) {
rows[rowCount] = {
__depth: 0,
__index: i,
${frame.fields.map((field, fieldIdx) => `${JSON.stringify(getDisplayName(field))}: values[${fieldIdx}][i]`).join(",")}
};
rowCount += 1;
if (rows[rowCount-1]['__nestedFrames']){
const childFrame = rows[rowCount-1]['__nestedFrames'];
rows[rowCount] = {__depth: 1, __index: i, data: childFrame[0]}
rowCount += 1;
}
}
return rows;
`;
const convert = new Function("frame", fnBody);
return convert(frame);
};
const compare = new Intl.Collator("en", { sensitivity: "base", numeric: true }).compare;
const strCompare = (a, b) => compare(String(a != null ? a : ""), String(b != null ? b : ""));
const numCompare = (a, b) => {
if (a === b) {
return 0;
}
if (a == null) {
return -1;
}
if (b == null) {
return 1;
}
return Number(a) - Number(b);
};
const frameCompare = (a, b) => {
var _a, _b;
return ((_a = a == null ? void 0 : a.value) != null ? _a : 0) - ((_b = b == null ? void 0 : b.value) != null ? _b : 0);
};
function getComparator(sortColumnType) {
switch (sortColumnType) {
// Handle sorting for frame type fields (sparklines)
case FieldType.frame:
return frameCompare;
case FieldType.time:
case FieldType.number:
case FieldType.boolean:
return numCompare;
case FieldType.string:
case FieldType.enum:
default:
return strCompare;
}
}
const TABLE_CELL_GAUGE_DISPLAY_MODES_TO_DISPLAY_MODES = {
[TableCellDisplayMode.BasicGauge]: BarGaugeDisplayMode.Basic,
[TableCellDisplayMode.GradientGauge]: BarGaugeDisplayMode.Gradient,
[TableCellDisplayMode.LcdGauge]: BarGaugeDisplayMode.Lcd
};
const TABLE_CELL_COLOR_BACKGROUND_DISPLAY_MODES_TO_DISPLAY_MODES = {
[TableCellDisplayMode.ColorBackground]: TableCellBackgroundDisplayMode.Gradient,
[TableCellDisplayMode.ColorBackgroundSolid]: TableCellBackgroundDisplayMode.Basic
};
function migrateTableDisplayModeToCellOptions(displayMode) {
switch (displayMode) {
// In the case of the gauge we move to a different option
case TableCellDisplayMode.BasicGauge:
case TableCellDisplayMode.GradientGauge:
case TableCellDisplayMode.LcdGauge:
return {
type: TableCellDisplayMode.Gauge,
mode: TABLE_CELL_GAUGE_DISPLAY_MODES_TO_DISPLAY_MODES[displayMode]
};
// Also true in the case of the color background
case TableCellDisplayMode.ColorBackground:
case TableCellDisplayMode.ColorBackgroundSolid:
return {
type: TableCellDisplayMode.ColorBackground,
mode: TABLE_CELL_COLOR_BACKGROUND_DISPLAY_MODES_TO_DISPLAY_MODES[displayMode]
};
// catching a nonsense case: `displayMode`: 'custom' should pre-date the CustomCell.
// if it doesn't, we need to just nope out and return an auto cell.
case TableCellDisplayMode.Custom:
return {
type: TableCellDisplayMode.Auto
};
default:
return {
type: displayMode
};
}
}
const getIsNestedTable = (fields) => fields.some(({ type }) => type === FieldType.nestedFrames);
const processNestedTableRows = (rows, processParents) => {
const parentRows = [];
const childRows = /* @__PURE__ */ new Map();
for (const row of rows) {
if (Number(row.__depth) === 0) {
parentRows.push(row);
} else {
childRows.set(Number(row.__index), row);
}
}
const processedParents = processParents(parentRows);
const result = [];
processedParents.forEach((row) => {
result.push(row);
const childRow = childRows.get(Number(row.__index));
if (childRow) {
result.push(childRow);
}
});
return result;
};
const getDisplayName = (field) => {
var _a, _b;
return (_b = (_a = field.state) == null ? void 0 : _a.displayName) != null ? _b : field.name;
};
function getVisibleFields(fields) {
return fields.filter((field) => {
var _a;
return field.type !== FieldType.nestedFrames && ((_a = field.config.custom) == null ? void 0 : _a.hidden) !== true;
});
}
function getColumnTypes(fields) {
return fields.reduce((acc, field) => {
var _a, _b, _c;
switch (field.type) {
case FieldType.nestedFrames:
return { ...acc, ...getColumnTypes((_c = (_b = (_a = field.values[0]) == null ? void 0 : _a[0]) == null ? void 0 : _b.fields) != null ? _c : []) };
default:
return { ...acc, [getDisplayName(field)]: field.type };
}
}, {});
}
function computeColWidths(fields, availWidth) {
let autoCount = 0;
let definedWidth = 0;
return fields.map((field) => {
var _a, _b;
const width = (_b = (_a = field.config.custom) == null ? void 0 : _a.width) != null ? _b : 0;
if (width === 0) {
autoCount++;
} else {
definedWidth += width;
}
return width;
}).map(
(width, i) => {
var _a, _b;
return width || Math.max((_b = (_a = fields[i].config.custom) == null ? void 0 : _a.minWidth) != null ? _b : COLUMN.DEFAULT_WIDTH, (availWidth - definedWidth) / autoCount);
}
);
}
function getApplyToRowBgFn(fields, theme) {
for (const field of fields) {
const cellOptions = getCellOptions(field);
const fieldDisplay = field.display;
if (fieldDisplay !== void 0 && cellOptions.type === TableCellDisplayMode.ColorBackground && cellOptions.applyToRow === true) {
return (rowIndex) => getCellColors(theme, cellOptions, fieldDisplay(field.values[rowIndex]));
}
}
}
function withDataLinksActionsTooltip(field, cellType) {
var _a, _b, _c, _d;
return cellType !== TableCellDisplayMode.DataLinks && cellType !== TableCellDisplayMode.Actions && ((_b = (_a = field.config.links) == null ? void 0 : _a.length) != null ? _b : 0) + ((_d = (_c = field.config.actions) == null ? void 0 : _c.length) != null ? _d : 0) > 1;
}
export { applySort, computeColWidths, extractPixelValue, frameToRecords, getAlignmentFactor, getApplyToRowBgFn, getCellColors, getCellLinks, getCellOptions, getColumnTypes, getComparator, getDefaultRowHeight, getDisplayName, getIsNestedTable, getMaxWrapCell, getTextAlign, getVisibleFields, isCellInspectEnabled, migrateTableDisplayModeToCellOptions, processNestedTableRows, shouldTextOverflow, shouldTextWrap, withDataLinksActionsTooltip };
//# sourceMappingURL=utils.mjs.map