mirror of
https://github.com/apache/superset.git
synced 2024-09-19 20:19:37 -04:00
feat(code refactoring): code refactoring
This commit is contained in:
parent
aa7ef1ad90
commit
c87a74841e
@ -1,17 +1,7 @@
|
||||
import React, { CSSProperties } from 'react';
|
||||
import React from 'react';
|
||||
import DataTable from '@airbnb/lunar/lib/components/DataTable';
|
||||
import { Renderers, RendererProps } from '@airbnb/lunar/lib/components/DataTable/types';
|
||||
import { HEIGHT_TO_PX } from '@airbnb/lunar/lib/components/DataTable/constants';
|
||||
import { FormDataMetric, Metric } from '@superset-ui/chart';
|
||||
|
||||
type ColumnType = {
|
||||
key: string;
|
||||
label: string;
|
||||
format?: (value: any) => string;
|
||||
type: 'metric' | 'string';
|
||||
maxValue?: number;
|
||||
minValue?: number;
|
||||
};
|
||||
import { Renderers } from '@airbnb/lunar/lib/components/DataTable/types';
|
||||
import { getRenderer, ColumnType, heightType, Cell } from './renderer';
|
||||
|
||||
type Props = {
|
||||
data: any[];
|
||||
@ -23,15 +13,9 @@ type Props = {
|
||||
[key: string]: any[];
|
||||
};
|
||||
includeSearch?: boolean;
|
||||
metrics: FormDataMetric[];
|
||||
onAddFilter?: (key: string, value: number[]) => void;
|
||||
onRemoveFilter?: (key: string, value: number[]) => void;
|
||||
orderDesc: boolean;
|
||||
pageLength: number | string;
|
||||
percentMetrics: string[];
|
||||
tableFilter: boolean;
|
||||
tableTimestampFormat: string;
|
||||
timeseriesLimitMetric: FormDataMetric;
|
||||
};
|
||||
|
||||
function NOOP(key: string, value: []) {}
|
||||
@ -47,17 +31,13 @@ const defaultProps = {
|
||||
|
||||
export type TableProps = Props & Readonly<typeof defaultProps>;
|
||||
|
||||
type Cell = {
|
||||
key: string;
|
||||
value: any;
|
||||
};
|
||||
|
||||
type TableState = {
|
||||
selectedCells: Set<string>;
|
||||
};
|
||||
|
||||
const NEGATIVE_COLOR = '#ff8787';
|
||||
const POSITIVE_COLOR = '#ced4da';
|
||||
function getCellHash(cell: Cell) {
|
||||
return `${cell.key}#${cell.value}`;
|
||||
}
|
||||
|
||||
class TableVis extends React.Component<TableProps, TableState> {
|
||||
static defaultProps = defaultProps;
|
||||
@ -77,7 +57,7 @@ class TableVis extends React.Component<TableProps, TableState> {
|
||||
return;
|
||||
}
|
||||
const newSelectedCells = new Set(Array.from(selectedCells));
|
||||
const cellHash = `${cell.key}#${cell.value}`;
|
||||
const cellHash = getCellHash(cell);
|
||||
if (newSelectedCells.has(cellHash)) {
|
||||
newSelectedCells.delete(cellHash);
|
||||
onRemoveFilter(cell.key, [cell.value]);
|
||||
@ -90,6 +70,11 @@ class TableVis extends React.Component<TableProps, TableState> {
|
||||
});
|
||||
};
|
||||
|
||||
isSelected = (cell: Cell) => {
|
||||
const { selectedCells } = this.state;
|
||||
return selectedCells.has(getCellHash(cell));
|
||||
};
|
||||
|
||||
static getDerivedStateFromProps: React.GetDerivedStateFromProps<TableProps, TableState> = (
|
||||
props: TableProps,
|
||||
state: TableState,
|
||||
@ -99,7 +84,12 @@ class TableVis extends React.Component<TableProps, TableState> {
|
||||
const newSelectedCells = new Set(Array.from(selectedCells));
|
||||
Object.keys(filters).forEach(key => {
|
||||
filters[key].forEach(value => {
|
||||
newSelectedCells.add(`${key}#${value}`);
|
||||
newSelectedCells.add(
|
||||
getCellHash({
|
||||
key,
|
||||
value,
|
||||
}),
|
||||
);
|
||||
});
|
||||
});
|
||||
return {
|
||||
@ -110,122 +100,30 @@ class TableVis extends React.Component<TableProps, TableState> {
|
||||
|
||||
render() {
|
||||
const {
|
||||
metrics,
|
||||
timeseriesLimitMetric,
|
||||
orderDesc,
|
||||
data,
|
||||
columns,
|
||||
alignPositiveNegative,
|
||||
colorPositiveNegative,
|
||||
columns,
|
||||
height,
|
||||
tableFilter,
|
||||
} = this.props;
|
||||
const { selectedCells } = this.state;
|
||||
|
||||
const sortByKey =
|
||||
timeseriesLimitMetric &&
|
||||
((timeseriesLimitMetric as Metric).label || (timeseriesLimitMetric as string));
|
||||
|
||||
let formattedData = data.map(row => ({
|
||||
data: row,
|
||||
}));
|
||||
|
||||
if (sortByKey) {
|
||||
formattedData = formattedData.sort((a, b) => {
|
||||
const delta = a.data[sortByKey] - b.data[sortByKey];
|
||||
if (orderDesc) {
|
||||
return -delta;
|
||||
}
|
||||
return delta;
|
||||
});
|
||||
if (metrics.indexOf(sortByKey) < 0) {
|
||||
formattedData = formattedData.map(row => {
|
||||
const data = { ...row.data };
|
||||
delete data[sortByKey];
|
||||
return {
|
||||
data,
|
||||
};
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
const heightType = 'small';
|
||||
const getRenderer = (column: ColumnType) => (props: RendererProps) => {
|
||||
const { key } = props;
|
||||
let value = props.row.rowData.data[key];
|
||||
const cellHash = `${key}#${value}`;
|
||||
const isMetric = metrics.indexOf(key) >= 0;
|
||||
let Parent;
|
||||
|
||||
if (isMetric) {
|
||||
let left = 0;
|
||||
let width = 0;
|
||||
if (alignPositiveNegative) {
|
||||
width = Math.abs(
|
||||
Math.round((value / Math.max(column.maxValue!, Math.abs(column.minValue!))) * 100),
|
||||
);
|
||||
} else {
|
||||
const posExtent = Math.abs(Math.max(column.maxValue!, 0));
|
||||
const negExtent = Math.abs(Math.min(column.minValue!, 0));
|
||||
const tot = posExtent + negExtent;
|
||||
left = Math.round((Math.min(negExtent + value, negExtent) / tot) * 100);
|
||||
width = Math.round((Math.abs(value) / tot) * 100);
|
||||
}
|
||||
const color = colorPositiveNegative && value < 0 ? NEGATIVE_COLOR : POSITIVE_COLOR;
|
||||
Parent = ({ children }: { children: React.ReactNode }) => {
|
||||
const boxStyle: CSSProperties = {
|
||||
margin: '0px -16px',
|
||||
backgroundColor: tableFilter && selectedCells.has(cellHash) ? '#ffec99' : undefined,
|
||||
};
|
||||
const boxContainerStyle: CSSProperties = {
|
||||
position: 'relative',
|
||||
height: HEIGHT_TO_PX[heightType],
|
||||
textAlign: isMetric ? 'right' : 'left',
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
margin: '0px 16px',
|
||||
};
|
||||
const barStyle: CSSProperties = {
|
||||
background: color,
|
||||
width: `${width}%`,
|
||||
left: `${left}%`,
|
||||
position: 'absolute',
|
||||
height: 24,
|
||||
};
|
||||
|
||||
return (
|
||||
<div style={boxStyle}>
|
||||
<div style={boxContainerStyle}>
|
||||
<div style={barStyle} />
|
||||
<div style={{ zIndex: 10, marginLeft: 'auto' }}>{children}</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
} else {
|
||||
Parent = ({ children }: { children: React.ReactNode }) => (
|
||||
<React.Fragment>{children}</React.Fragment>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div
|
||||
onClick={this.handleCellSelected({
|
||||
key,
|
||||
value,
|
||||
})}
|
||||
>
|
||||
<Parent>{column.format ? column.format(value) : value}</Parent>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
const renderers: Renderers = {};
|
||||
|
||||
columns.forEach(column => {
|
||||
renderers[column.key] = getRenderer(column);
|
||||
renderers[column.key] = getRenderer({
|
||||
column,
|
||||
alignPositiveNegative,
|
||||
colorPositiveNegative,
|
||||
enableFilter: tableFilter,
|
||||
isSelected: this.isSelected,
|
||||
handleCellSelected: this.handleCellSelected,
|
||||
});
|
||||
});
|
||||
|
||||
return <DataTable data={formattedData} zebra rowHeight={heightType} renderers={renderers} />;
|
||||
return (
|
||||
<DataTable data={data} zebra rowHeight={heightType} renderers={renderers} height={height} />
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -22,6 +22,12 @@ import { ChartProps, FormDataMetric, Metric } from '@superset-ui/chart';
|
||||
import { getNumberFormatter, NumberFormats, NumberFormatter } from '@superset-ui/number-format';
|
||||
import { getTimeFormatter, TimeFormatter } from '@superset-ui/time-format';
|
||||
|
||||
const DTTM_ALIAS = '__timestamp';
|
||||
|
||||
type PlainObject = {
|
||||
[key: string]: any;
|
||||
};
|
||||
|
||||
export default function transformProps(chartProps: ChartProps) {
|
||||
const { height, datasource, filters, formData, onAddFilter, payload } = chartProps;
|
||||
const {
|
||||
@ -47,9 +53,38 @@ export default function transformProps(chartProps: ChartProps) {
|
||||
.filter(m => typeof records[0][m as string] === 'number');
|
||||
|
||||
const dataArray: {
|
||||
[key: string]: any;
|
||||
[key: string]: any[];
|
||||
} = {};
|
||||
|
||||
const sortByKey =
|
||||
timeseriesLimitMetric &&
|
||||
((timeseriesLimitMetric as Metric).label || (timeseriesLimitMetric as string));
|
||||
|
||||
let formattedData: {
|
||||
data: PlainObject;
|
||||
}[] = records.map((row: PlainObject) => ({
|
||||
data: row,
|
||||
}));
|
||||
|
||||
if (sortByKey) {
|
||||
formattedData = formattedData.sort((a, b) => {
|
||||
const delta = a.data[sortByKey] - b.data[sortByKey];
|
||||
if (orderDesc) {
|
||||
return -delta;
|
||||
}
|
||||
return delta;
|
||||
});
|
||||
if (metrics.indexOf(sortByKey) < 0) {
|
||||
formattedData = formattedData.map(row => {
|
||||
const data = { ...row.data };
|
||||
delete data[sortByKey];
|
||||
return {
|
||||
data,
|
||||
};
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
metrics.forEach(metric => {
|
||||
const arr = [];
|
||||
for (let i = 0; i < records.length; i += 1) {
|
||||
@ -91,7 +126,7 @@ export default function transformProps(chartProps: ChartProps) {
|
||||
}
|
||||
}
|
||||
|
||||
if (key === '__timestamp') {
|
||||
if (key === DTTM_ALIAS) {
|
||||
formatFunction = tsFormatter;
|
||||
}
|
||||
|
||||
@ -116,19 +151,15 @@ export default function transformProps(chartProps: ChartProps) {
|
||||
|
||||
return {
|
||||
height,
|
||||
data: records,
|
||||
data: formattedData,
|
||||
alignPositiveNegative: alignPn,
|
||||
colorPositiveNegative: colorPn,
|
||||
columns: processedColumns,
|
||||
filters,
|
||||
includeSearch,
|
||||
metrics,
|
||||
onAddFilter,
|
||||
orderDesc,
|
||||
pageLength: pageLength && parseInt(pageLength, 10),
|
||||
percentMetrics,
|
||||
tableFilter,
|
||||
tableTimestampFormat,
|
||||
timeseriesLimitMetric,
|
||||
};
|
||||
}
|
||||
|
@ -0,0 +1,106 @@
|
||||
import React, { CSSProperties } from 'react';
|
||||
import { HEIGHT_TO_PX } from '@airbnb/lunar/lib/components/DataTable/constants';
|
||||
import { RendererProps } from '@airbnb/lunar/lib/components/DataTable/types';
|
||||
|
||||
const NEGATIVE_COLOR = '#ff8787';
|
||||
const POSITIVE_COLOR = '#ced4da';
|
||||
const SELECTION_COLOR = '#ffec99';
|
||||
|
||||
export const heightType = 'micro';
|
||||
|
||||
export type ColumnType = {
|
||||
key: string;
|
||||
label: string;
|
||||
format?: (value: any) => string;
|
||||
type: 'metric' | 'string';
|
||||
maxValue?: number;
|
||||
minValue?: number;
|
||||
};
|
||||
|
||||
export type Cell = {
|
||||
key: string;
|
||||
value: any;
|
||||
};
|
||||
|
||||
export const getRenderer = ({
|
||||
column,
|
||||
alignPositiveNegative,
|
||||
colorPositiveNegative,
|
||||
enableFilter,
|
||||
isSelected,
|
||||
handleCellSelected,
|
||||
}: {
|
||||
column: ColumnType;
|
||||
alignPositiveNegative: boolean;
|
||||
colorPositiveNegative: boolean;
|
||||
enableFilter: boolean;
|
||||
isSelected: (cell: Cell) => boolean;
|
||||
handleCellSelected: (cell: Cell) => any;
|
||||
}) => (props: RendererProps) => {
|
||||
const { key } = props;
|
||||
let value = props.row.rowData.data[key];
|
||||
const isMetric = column.type === 'metric';
|
||||
let Parent;
|
||||
|
||||
if (isMetric) {
|
||||
let left = 0;
|
||||
let width = 0;
|
||||
if (alignPositiveNegative) {
|
||||
width = Math.abs(
|
||||
Math.round((value / Math.max(column.maxValue!, Math.abs(column.minValue!))) * 100),
|
||||
);
|
||||
} else {
|
||||
const posExtent = Math.abs(Math.max(column.maxValue!, 0));
|
||||
const negExtent = Math.abs(Math.min(column.minValue!, 0));
|
||||
const tot = posExtent + negExtent;
|
||||
left = Math.round((Math.min(negExtent + value, negExtent) / tot) * 100);
|
||||
width = Math.round((Math.abs(value) / tot) * 100);
|
||||
}
|
||||
const color = colorPositiveNegative && value < 0 ? NEGATIVE_COLOR : POSITIVE_COLOR;
|
||||
Parent = ({ children }: { children: React.ReactNode }) => {
|
||||
const boxStyle: CSSProperties = {
|
||||
margin: '0px -16px',
|
||||
backgroundColor: enableFilter && isSelected({ key, value }) ? SELECTION_COLOR : undefined,
|
||||
};
|
||||
const boxContainerStyle: CSSProperties = {
|
||||
position: 'relative',
|
||||
height: HEIGHT_TO_PX[heightType],
|
||||
textAlign: isMetric ? 'right' : 'left',
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
margin: '0px 16px',
|
||||
};
|
||||
const barStyle: CSSProperties = {
|
||||
background: color,
|
||||
width: `${width}%`,
|
||||
left: `${left}%`,
|
||||
position: 'absolute',
|
||||
height: HEIGHT_TO_PX[heightType] / 2,
|
||||
};
|
||||
|
||||
return (
|
||||
<div style={boxStyle}>
|
||||
<div style={boxContainerStyle}>
|
||||
<div style={barStyle} />
|
||||
<div style={{ zIndex: 10, marginLeft: 'auto' }}>{children}</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
} else {
|
||||
Parent = ({ children }: { children: React.ReactNode }) => (
|
||||
<React.Fragment>{children}</React.Fragment>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div
|
||||
onClick={handleCellSelected({
|
||||
key,
|
||||
value,
|
||||
})}
|
||||
>
|
||||
<Parent>{column.format ? column.format(value) : value}</Parent>
|
||||
</div>
|
||||
);
|
||||
};
|
Loading…
Reference in New Issue
Block a user