2020-09-01 23:06:35 +08:00
|
|
|
import { Column, Row } from 'react-table';
|
|
|
|
|
import memoizeOne from 'memoize-one';
|
2020-09-25 02:09:01 +08:00
|
|
|
import { ContentPosition } from 'csstype';
|
2020-09-01 23:06:35 +08:00
|
|
|
import {
|
|
|
|
|
DataFrame,
|
|
|
|
|
Field,
|
|
|
|
|
FieldType,
|
|
|
|
|
formattedValueToString,
|
|
|
|
|
getFieldDisplayName,
|
|
|
|
|
SelectableValue,
|
|
|
|
|
} from '@grafana/data';
|
|
|
|
|
|
2020-01-13 18:12:19 +08:00
|
|
|
import { DefaultCell } from './DefaultCell';
|
2019-12-23 13:22:54 +08:00
|
|
|
import { BarGaugeCell } from './BarGaugeCell';
|
2020-09-25 02:09:01 +08:00
|
|
|
import { TableCellDisplayMode, TableFieldOptions } from './types';
|
2020-04-21 02:10:03 +08:00
|
|
|
import { JSONViewCell } from './JSONViewCell';
|
2020-09-25 02:09:01 +08:00
|
|
|
import { ImageCell } from './ImageCell';
|
2019-12-23 13:22:54 +08:00
|
|
|
|
2020-09-25 02:09:01 +08:00
|
|
|
export function getTextAlign(field?: Field): ContentPosition {
|
2020-04-01 20:24:17 +08:00
|
|
|
if (!field) {
|
2020-09-25 02:09:01 +08:00
|
|
|
return 'flex-start';
|
2020-04-01 20:24:17 +08:00
|
|
|
}
|
|
|
|
|
|
2019-12-23 13:22:54 +08:00
|
|
|
if (field.config.custom) {
|
|
|
|
|
const custom = field.config.custom as TableFieldOptions;
|
|
|
|
|
|
|
|
|
|
switch (custom.align) {
|
|
|
|
|
case 'right':
|
2020-09-25 02:09:01 +08:00
|
|
|
return 'flex-end';
|
2019-12-23 13:22:54 +08:00
|
|
|
case 'left':
|
2020-09-25 02:09:01 +08:00
|
|
|
return 'flex-start';
|
2019-12-23 13:22:54 +08:00
|
|
|
case 'center':
|
|
|
|
|
return 'center';
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (field.type === FieldType.number) {
|
2020-09-25 02:09:01 +08:00
|
|
|
return 'flex-end';
|
2019-12-23 13:22:54 +08:00
|
|
|
}
|
|
|
|
|
|
2020-09-25 02:09:01 +08:00
|
|
|
return 'flex-start';
|
2019-12-23 13:22:54 +08:00
|
|
|
}
|
|
|
|
|
|
2020-02-28 16:26:20 +08:00
|
|
|
export function getColumns(data: DataFrame, availableWidth: number, columnMinWidth: number): Column[] {
|
2020-06-08 20:28:45 +08:00
|
|
|
const columns: any[] = [];
|
2019-12-23 13:22:54 +08:00
|
|
|
let fieldCountWithoutWidth = data.fields.length;
|
|
|
|
|
|
2020-05-13 21:34:23 +08:00
|
|
|
for (const [fieldIndex, field] of data.fields.entries()) {
|
2019-12-23 13:22:54 +08:00
|
|
|
const fieldTableOptions = (field.config.custom || {}) as TableFieldOptions;
|
2020-04-20 15:27:40 +08:00
|
|
|
|
2020-05-13 21:34:23 +08:00
|
|
|
if (fieldTableOptions.hidden) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
2019-12-23 13:22:54 +08:00
|
|
|
if (fieldTableOptions.width) {
|
|
|
|
|
availableWidth -= fieldTableOptions.width;
|
|
|
|
|
fieldCountWithoutWidth -= 1;
|
|
|
|
|
}
|
|
|
|
|
|
2020-06-08 20:28:45 +08:00
|
|
|
const selectSortType = (type: FieldType): string => {
|
|
|
|
|
switch (type) {
|
|
|
|
|
case FieldType.number:
|
2021-05-31 12:12:56 +08:00
|
|
|
return 'number';
|
2020-06-08 20:28:45 +08:00
|
|
|
case FieldType.time:
|
|
|
|
|
return 'basic';
|
|
|
|
|
default:
|
2021-04-07 21:28:51 +08:00
|
|
|
return 'alphanumeric-insensitive';
|
2020-06-08 20:28:45 +08:00
|
|
|
}
|
|
|
|
|
};
|
2020-09-25 02:09:01 +08:00
|
|
|
|
2020-04-21 02:10:03 +08:00
|
|
|
const Cell = getCellComponent(fieldTableOptions.displayMode, field);
|
2020-01-13 18:12:19 +08:00
|
|
|
columns.push({
|
2019-12-23 13:22:54 +08:00
|
|
|
Cell,
|
2020-04-20 15:27:40 +08:00
|
|
|
id: fieldIndex.toString(),
|
2020-05-12 19:52:53 +08:00
|
|
|
Header: getFieldDisplayName(field, data),
|
2020-04-20 15:27:40 +08:00
|
|
|
accessor: (row: any, i: number) => {
|
|
|
|
|
return field.values.get(i);
|
|
|
|
|
},
|
2020-06-08 20:28:45 +08:00
|
|
|
sortType: selectSortType(field.type),
|
2019-12-23 13:22:54 +08:00
|
|
|
width: fieldTableOptions.width,
|
2020-04-20 15:27:40 +08:00
|
|
|
minWidth: 50,
|
2020-11-02 14:26:58 +08:00
|
|
|
filter: memoizeOne(filterByValue(field)),
|
2020-09-25 02:09:01 +08:00
|
|
|
justifyContent: getTextAlign(field),
|
2019-12-23 13:22:54 +08:00
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// divide up the rest of the space
|
|
|
|
|
const sharedWidth = availableWidth / fieldCountWithoutWidth;
|
2020-01-13 18:12:19 +08:00
|
|
|
for (const column of columns) {
|
2019-12-23 13:22:54 +08:00
|
|
|
if (!column.width) {
|
2020-02-28 16:26:20 +08:00
|
|
|
column.width = Math.max(sharedWidth, columnMinWidth);
|
2019-12-23 13:22:54 +08:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2020-01-13 18:12:19 +08:00
|
|
|
return columns;
|
|
|
|
|
}
|
|
|
|
|
|
2020-04-21 02:10:03 +08:00
|
|
|
function getCellComponent(displayMode: TableCellDisplayMode, field: Field) {
|
2020-01-13 18:12:19 +08:00
|
|
|
switch (displayMode) {
|
2020-04-09 13:42:31 +08:00
|
|
|
case TableCellDisplayMode.ColorText:
|
2020-01-13 18:12:19 +08:00
|
|
|
case TableCellDisplayMode.ColorBackground:
|
2020-09-25 02:09:01 +08:00
|
|
|
return DefaultCell;
|
|
|
|
|
case TableCellDisplayMode.Image:
|
|
|
|
|
return ImageCell;
|
2020-01-13 18:12:19 +08:00
|
|
|
case TableCellDisplayMode.LcdGauge:
|
2020-07-26 22:52:42 +08:00
|
|
|
case TableCellDisplayMode.BasicGauge:
|
2020-01-13 18:12:19 +08:00
|
|
|
case TableCellDisplayMode.GradientGauge:
|
|
|
|
|
return BarGaugeCell;
|
2020-04-21 02:10:03 +08:00
|
|
|
case TableCellDisplayMode.JSONView:
|
|
|
|
|
return JSONViewCell;
|
2020-01-13 18:12:19 +08:00
|
|
|
}
|
2020-04-21 02:10:03 +08:00
|
|
|
|
|
|
|
|
// Default or Auto
|
|
|
|
|
if (field.type === FieldType.other) {
|
|
|
|
|
return JSONViewCell;
|
|
|
|
|
}
|
|
|
|
|
return DefaultCell;
|
2019-12-23 13:22:54 +08:00
|
|
|
}
|
2020-04-09 13:42:31 +08:00
|
|
|
|
2020-11-02 14:26:58 +08:00
|
|
|
export function filterByValue(field?: Field) {
|
2021-01-20 14:59:48 +08:00
|
|
|
return function (rows: Row[], id: string, filterValues?: SelectableValue[]) {
|
2020-11-02 14:26:58 +08:00
|
|
|
if (rows.length === 0) {
|
|
|
|
|
return rows;
|
|
|
|
|
}
|
2020-09-01 23:06:35 +08:00
|
|
|
|
2020-11-02 14:26:58 +08:00
|
|
|
if (!filterValues) {
|
|
|
|
|
return rows;
|
|
|
|
|
}
|
2020-09-01 23:06:35 +08:00
|
|
|
|
2020-11-02 14:26:58 +08:00
|
|
|
if (!field) {
|
|
|
|
|
return rows;
|
2020-09-01 23:06:35 +08:00
|
|
|
}
|
|
|
|
|
|
2021-01-20 14:59:48 +08:00
|
|
|
return rows.filter((row) => {
|
2020-11-02 14:26:58 +08:00
|
|
|
if (!row.values.hasOwnProperty(id)) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
const value = rowToFieldValue(row, field);
|
2021-01-20 14:59:48 +08:00
|
|
|
return filterValues.find((filter) => filter.value === value) !== undefined;
|
2020-11-02 14:26:58 +08:00
|
|
|
});
|
|
|
|
|
};
|
2020-09-01 23:06:35 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export function calculateUniqueFieldValues(rows: any[], field?: Field) {
|
|
|
|
|
if (!field || rows.length === 0) {
|
|
|
|
|
return {};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const set: Record<string, any> = {};
|
|
|
|
|
|
|
|
|
|
for (let index = 0; index < rows.length; index++) {
|
2020-11-02 14:26:58 +08:00
|
|
|
const value = rowToFieldValue(rows[index], field);
|
|
|
|
|
set[value || '(Blanks)'] = value;
|
2020-09-01 23:06:35 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return set;
|
|
|
|
|
}
|
|
|
|
|
|
2020-11-02 14:26:58 +08:00
|
|
|
export function rowToFieldValue(row: any, field?: Field): string {
|
|
|
|
|
if (!field || !row) {
|
|
|
|
|
return '';
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const fieldValue = field.values.get(row.index);
|
|
|
|
|
const displayValue = field.display ? field.display(fieldValue) : fieldValue;
|
|
|
|
|
const value = field.display ? formattedValueToString(displayValue) : displayValue;
|
|
|
|
|
|
|
|
|
|
return value;
|
|
|
|
|
}
|
|
|
|
|
|
2020-09-01 23:06:35 +08:00
|
|
|
export function valuesToOptions(unique: Record<string, any>): SelectableValue[] {
|
|
|
|
|
return Object.keys(unique)
|
|
|
|
|
.reduce((all, key) => all.concat({ value: unique[key], label: key }), [] as SelectableValue[])
|
|
|
|
|
.sort(sortOptions);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export function sortOptions(a: SelectableValue, b: SelectableValue): number {
|
|
|
|
|
if (a.label === undefined && b.label === undefined) {
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (a.label === undefined && b.label !== undefined) {
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (a.label !== undefined && b.label === undefined) {
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (a.label! < b.label!) {
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (a.label! > b.label!) {
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export function getFilteredOptions(options: SelectableValue[], filterValues?: SelectableValue[]): SelectableValue[] {
|
|
|
|
|
if (!filterValues) {
|
|
|
|
|
return [];
|
|
|
|
|
}
|
|
|
|
|
|
2021-01-20 14:59:48 +08:00
|
|
|
return options.filter((option) => filterValues.some((filtered) => filtered.value === option.value));
|
2020-09-01 23:06:35 +08:00
|
|
|
}
|
2021-04-07 21:28:51 +08:00
|
|
|
|
|
|
|
|
export function sortCaseInsensitive(a: Row<any>, b: Row<any>, id: string) {
|
|
|
|
|
return String(a.values[id]).localeCompare(String(b.values[id]), undefined, { sensitivity: 'base' });
|
|
|
|
|
}
|
2021-05-31 12:12:56 +08:00
|
|
|
|
|
|
|
|
// sortNumber needs to have great performance as it is called a lot
|
|
|
|
|
export function sortNumber(rowA: Row<any>, rowB: Row<any>, id: string) {
|
|
|
|
|
const a = toNumber(rowA.values[id]);
|
|
|
|
|
const b = toNumber(rowB.values[id]);
|
|
|
|
|
return a === b ? 0 : a > b ? 1 : -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function toNumber(value: any): number {
|
|
|
|
|
if (typeof value === 'number') {
|
|
|
|
|
return value;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (value === null || value === undefined || value === '' || isNaN(value)) {
|
|
|
|
|
return Number.NEGATIVE_INFINITY;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return Number(value);
|
|
|
|
|
}
|