mirror of
https://github.com/grafana/grafana.git
synced 2025-07-31 03:02:31 +08:00

* only render icons on hover * Table: Optimization - remove plaintext cell wrapper (#76916) * remove inner wrapper div from plain text cells * reuse result of typeof value === 'string' --------- Co-authored-by: Galen <galen.kistler@grafana.com> --------- Co-authored-by: Leon Sorokin <leeoniya@gmail.com>
142 lines
5.2 KiB
TypeScript
142 lines
5.2 KiB
TypeScript
import { cx } from '@emotion/css';
|
|
import React, { ReactElement, useState } from 'react';
|
|
import tinycolor from 'tinycolor2';
|
|
|
|
import { DisplayValue, formattedValueToString } from '@grafana/data';
|
|
import { TableCellBackgroundDisplayMode, TableCellDisplayMode } from '@grafana/schema';
|
|
|
|
import { useStyles2 } from '../../themes';
|
|
import { getCellLinks, getTextColorForAlphaBackground } from '../../utils';
|
|
import { clearLinkButtonStyles } from '../Button';
|
|
import { DataLinksContextMenu } from '../DataLinks/DataLinksContextMenu';
|
|
|
|
import { CellActions } from './CellActions';
|
|
import { TableStyles } from './styles';
|
|
import { TableCellProps, CustomCellRendererProps, TableCellOptions } from './types';
|
|
import { getCellOptions } from './utils';
|
|
|
|
export const DefaultCell = (props: TableCellProps) => {
|
|
const { field, cell, tableStyles, row, cellProps, frame } = props;
|
|
|
|
const inspectEnabled = Boolean(field.config.custom?.inspect);
|
|
const displayValue = field.display!(cell.value);
|
|
|
|
const showFilters = props.onCellFilterAdded && field.config.filterable;
|
|
const showActions = (showFilters && cell.value !== undefined) || inspectEnabled;
|
|
const cellOptions = getCellOptions(field);
|
|
const hasLinks = Boolean(getCellLinks(field, row)?.length);
|
|
const clearButtonStyle = useStyles2(clearLinkButtonStyles);
|
|
const [hover, setHover] = useState(false);
|
|
let value: string | ReactElement;
|
|
|
|
const onMouseLeave = () => {
|
|
setHover(false);
|
|
};
|
|
const onMouseEnter = () => {
|
|
setHover(true);
|
|
};
|
|
|
|
if (cellOptions.type === TableCellDisplayMode.Custom) {
|
|
const CustomCellComponent: React.ComponentType<CustomCellRendererProps> = cellOptions.cellComponent;
|
|
value = <CustomCellComponent field={field} value={cell.value} rowIndex={row.index} frame={frame} />;
|
|
} else {
|
|
if (React.isValidElement(cell.value)) {
|
|
value = cell.value;
|
|
} else {
|
|
value = formattedValueToString(displayValue);
|
|
}
|
|
}
|
|
|
|
const isStringValue = typeof value === 'string';
|
|
|
|
const cellStyle = getCellStyle(tableStyles, cellOptions, displayValue, inspectEnabled, isStringValue);
|
|
|
|
if (isStringValue && cellProps.style?.justifyContent === 'flex-end') {
|
|
cellProps.style!.textAlign = 'right';
|
|
}
|
|
|
|
return (
|
|
<div
|
|
{...cellProps}
|
|
onMouseEnter={showActions ? onMouseEnter : undefined}
|
|
onMouseLeave={showActions ? onMouseLeave : undefined}
|
|
className={cellStyle}
|
|
>
|
|
{!hasLinks && (isStringValue ? `${value}` : <div className={tableStyles.cellText}>{value}</div>)}
|
|
|
|
{hasLinks && (
|
|
<DataLinksContextMenu links={() => getCellLinks(field, row) || []}>
|
|
{(api) => {
|
|
if (api.openMenu) {
|
|
return (
|
|
<button
|
|
className={cx(clearButtonStyle, getLinkStyle(tableStyles, cellOptions, api.targetClassName))}
|
|
onClick={api.openMenu}
|
|
>
|
|
{value}
|
|
</button>
|
|
);
|
|
} else {
|
|
return <div className={getLinkStyle(tableStyles, cellOptions, api.targetClassName)}>{value}</div>;
|
|
}
|
|
}}
|
|
</DataLinksContextMenu>
|
|
)}
|
|
|
|
{hover && showActions && <CellActions {...props} previewMode="text" showFilters={showFilters} />}
|
|
</div>
|
|
);
|
|
};
|
|
|
|
function getCellStyle(
|
|
tableStyles: TableStyles,
|
|
cellOptions: TableCellOptions,
|
|
displayValue: DisplayValue,
|
|
disableOverflowOnHover = false,
|
|
isStringValue = false
|
|
) {
|
|
// How much to darken elements depends upon if we're in dark mode
|
|
const darkeningFactor = tableStyles.theme.isDark ? 1 : -0.7;
|
|
|
|
// Setup color variables
|
|
let textColor: string | undefined = undefined;
|
|
let bgColor: string | undefined = undefined;
|
|
|
|
if (cellOptions.type === TableCellDisplayMode.ColorText) {
|
|
textColor = displayValue.color;
|
|
} else if (cellOptions.type === TableCellDisplayMode.ColorBackground) {
|
|
const mode = cellOptions.mode ?? TableCellBackgroundDisplayMode.Gradient;
|
|
|
|
if (mode === TableCellBackgroundDisplayMode.Basic) {
|
|
textColor = getTextColorForAlphaBackground(displayValue.color!, tableStyles.theme.isDark);
|
|
bgColor = tinycolor(displayValue.color).toRgbString();
|
|
} else if (mode === TableCellBackgroundDisplayMode.Gradient) {
|
|
const bgColor2 = tinycolor(displayValue.color)
|
|
.darken(10 * darkeningFactor)
|
|
.spin(5);
|
|
textColor = getTextColorForAlphaBackground(displayValue.color!, tableStyles.theme.isDark);
|
|
bgColor = `linear-gradient(120deg, ${bgColor2.toRgbString()}, ${displayValue.color})`;
|
|
}
|
|
}
|
|
|
|
// If we have definied colors return those styles
|
|
// Otherwise we return default styles
|
|
if (textColor !== undefined || bgColor !== undefined) {
|
|
return tableStyles.buildCellContainerStyle(textColor, bgColor, !disableOverflowOnHover, isStringValue);
|
|
}
|
|
|
|
if (isStringValue) {
|
|
return disableOverflowOnHover ? tableStyles.cellContainerTextNoOverflow : tableStyles.cellContainerText;
|
|
} else {
|
|
return disableOverflowOnHover ? tableStyles.cellContainerNoOverflow : tableStyles.cellContainer;
|
|
}
|
|
}
|
|
|
|
function getLinkStyle(tableStyles: TableStyles, cellOptions: TableCellOptions, targetClassName: string | undefined) {
|
|
if (cellOptions.type === TableCellDisplayMode.Auto) {
|
|
return cx(tableStyles.cellLink, targetClassName);
|
|
}
|
|
|
|
return cx(tableStyles.cellLinkForColoredCell, targetClassName);
|
|
}
|