mirror of
https://github.com/apache/superset.git
synced 2024-09-17 11:09:47 -04:00
chore: refactor FilterableTable to functional component (#21136)
This commit is contained in:
parent
25c2b7f761
commit
7c0963f6ae
@ -17,7 +17,7 @@
|
|||||||
* under the License.
|
* under the License.
|
||||||
*/
|
*/
|
||||||
import JSONbig from 'json-bigint';
|
import JSONbig from 'json-bigint';
|
||||||
import React, { PureComponent } from 'react';
|
import React, { useEffect, useRef, useState } from 'react';
|
||||||
import JSONTree from 'react-json-tree';
|
import JSONTree from 'react-json-tree';
|
||||||
import {
|
import {
|
||||||
AutoSizer,
|
AutoSizer,
|
||||||
@ -33,8 +33,7 @@ import {
|
|||||||
getMultipleTextDimensions,
|
getMultipleTextDimensions,
|
||||||
t,
|
t,
|
||||||
styled,
|
styled,
|
||||||
SupersetTheme,
|
useTheme,
|
||||||
withTheme,
|
|
||||||
} from '@superset-ui/core';
|
} from '@superset-ui/core';
|
||||||
import Button from '../Button';
|
import Button from '../Button';
|
||||||
import CopyToClipboard from '../CopyToClipboard';
|
import CopyToClipboard from '../CopyToClipboard';
|
||||||
@ -188,98 +187,65 @@ export interface FilterableTableProps {
|
|||||||
orderedColumnKeys: string[];
|
orderedColumnKeys: string[];
|
||||||
data: Record<string, unknown>[];
|
data: Record<string, unknown>[];
|
||||||
height: number;
|
height: number;
|
||||||
filterText: string;
|
filterText?: string;
|
||||||
headerHeight: number;
|
headerHeight?: number;
|
||||||
overscanColumnCount: number;
|
overscanColumnCount?: number;
|
||||||
overscanRowCount: number;
|
overscanRowCount?: number;
|
||||||
rowHeight: number;
|
rowHeight?: number;
|
||||||
striped: boolean;
|
striped?: boolean;
|
||||||
expandedColumns: string[];
|
expandedColumns?: string[];
|
||||||
theme: SupersetTheme;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
interface FilterableTableState {
|
const FilterableTable = ({
|
||||||
sortBy?: string;
|
orderedColumnKeys,
|
||||||
sortDirection?: SortDirectionType;
|
data,
|
||||||
fitted: boolean;
|
height,
|
||||||
displayedList: Datum[];
|
filterText = '',
|
||||||
}
|
headerHeight = 32,
|
||||||
|
overscanColumnCount = 10,
|
||||||
|
overscanRowCount = 10,
|
||||||
|
rowHeight = 32,
|
||||||
|
striped = true,
|
||||||
|
expandedColumns = [],
|
||||||
|
}: FilterableTableProps) => {
|
||||||
|
const formatTableData = (data: Record<string, unknown>[]): Datum[] =>
|
||||||
|
data.map(row => {
|
||||||
|
const newRow = {};
|
||||||
|
Object.entries(row).forEach(([key, val]) => {
|
||||||
|
if (['string', 'number'].indexOf(typeof val) >= 0) {
|
||||||
|
newRow[key] = val;
|
||||||
|
} else {
|
||||||
|
newRow[key] = val === null ? null : JSONbig.stringify(val);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return newRow;
|
||||||
|
});
|
||||||
|
|
||||||
class FilterableTable extends PureComponent<
|
const [sortByState, setSortByState] = useState<string | undefined>(undefined);
|
||||||
FilterableTableProps,
|
const [sortDirectionState, setSortDirectionState] = useState<
|
||||||
FilterableTableState
|
SortDirectionType | undefined
|
||||||
> {
|
>(undefined);
|
||||||
static defaultProps = {
|
const [fitted, setFitted] = useState(false);
|
||||||
filterText: '',
|
const [list] = useState<Datum[]>(() => formatTableData(data));
|
||||||
headerHeight: 32,
|
const [displayedList, setDisplayedList] = useState<Datum[]>(list);
|
||||||
overscanColumnCount: 10,
|
|
||||||
overscanRowCount: 10,
|
|
||||||
rowHeight: 32,
|
|
||||||
striped: true,
|
|
||||||
expandedColumns: [],
|
|
||||||
};
|
|
||||||
|
|
||||||
list: Datum[];
|
// columns that have complex type and were expanded into sub columns
|
||||||
|
const [complexColumns] = useState<Record<string, boolean>>(
|
||||||
complexColumns: Record<string, boolean>;
|
orderedColumnKeys.reduce(
|
||||||
|
|
||||||
widthsForColumnsByKey: Record<string, number>;
|
|
||||||
|
|
||||||
totalTableWidth: number;
|
|
||||||
|
|
||||||
totalTableHeight: number;
|
|
||||||
|
|
||||||
container: React.RefObject<HTMLDivElement>;
|
|
||||||
|
|
||||||
jsonTreeTheme: Record<string, string>;
|
|
||||||
|
|
||||||
constructor(props: FilterableTableProps) {
|
|
||||||
super(props);
|
|
||||||
this.list = this.formatTableData(props.data);
|
|
||||||
this.addJsonModal = this.addJsonModal.bind(this);
|
|
||||||
this.getCellContent = this.getCellContent.bind(this);
|
|
||||||
this.renderGridCell = this.renderGridCell.bind(this);
|
|
||||||
this.renderGridCellHeader = this.renderGridCellHeader.bind(this);
|
|
||||||
this.renderGrid = this.renderGrid.bind(this);
|
|
||||||
this.renderTableCell = this.renderTableCell.bind(this);
|
|
||||||
this.renderTableHeader = this.renderTableHeader.bind(this);
|
|
||||||
this.sortResults = this.sortResults.bind(this);
|
|
||||||
this.renderTable = this.renderTable.bind(this);
|
|
||||||
this.rowClassName = this.rowClassName.bind(this);
|
|
||||||
this.sort = this.sort.bind(this);
|
|
||||||
this.getJsonTreeTheme = this.getJsonTreeTheme.bind(this);
|
|
||||||
|
|
||||||
// columns that have complex type and were expanded into sub columns
|
|
||||||
this.complexColumns = props.orderedColumnKeys.reduce(
|
|
||||||
(obj, key) => ({
|
(obj, key) => ({
|
||||||
...obj,
|
...obj,
|
||||||
[key]: props.expandedColumns.some(name => name.startsWith(`${key}.`)),
|
[key]: expandedColumns.some(name => name.startsWith(`${key}.`)),
|
||||||
}),
|
}),
|
||||||
{},
|
{},
|
||||||
);
|
),
|
||||||
|
);
|
||||||
|
|
||||||
this.widthsForColumnsByKey = this.getWidthsForColumns();
|
const theme = useTheme();
|
||||||
this.totalTableWidth = props.orderedColumnKeys
|
const [jsonTreeTheme, setJsonTreeTheme] = useState<Record<string, string>>();
|
||||||
.map(key => this.widthsForColumnsByKey[key])
|
|
||||||
.reduce((curr, next) => curr + next);
|
|
||||||
this.totalTableHeight = props.height;
|
|
||||||
|
|
||||||
this.state = {
|
const getJsonTreeTheme = () => {
|
||||||
fitted: false,
|
if (!jsonTreeTheme) {
|
||||||
displayedList: [...this.list],
|
setJsonTreeTheme({
|
||||||
};
|
|
||||||
|
|
||||||
this.container = React.createRef();
|
|
||||||
}
|
|
||||||
|
|
||||||
componentDidMount() {
|
|
||||||
this.fitTableToWidthIfNeeded();
|
|
||||||
}
|
|
||||||
|
|
||||||
getJsonTreeTheme() {
|
|
||||||
if (!this.jsonTreeTheme) {
|
|
||||||
const { theme } = this.props;
|
|
||||||
this.jsonTreeTheme = {
|
|
||||||
base00: theme.colors.grayscale.dark2,
|
base00: theme.colors.grayscale.dark2,
|
||||||
base01: theme.colors.grayscale.dark1,
|
base01: theme.colors.grayscale.dark1,
|
||||||
base02: theme.colors.grayscale.base,
|
base02: theme.colors.grayscale.base,
|
||||||
@ -296,22 +262,20 @@ class FilterableTable extends PureComponent<
|
|||||||
base0D: theme.colors.primary.base,
|
base0D: theme.colors.primary.base,
|
||||||
base0E: theme.colors.primary.dark1,
|
base0E: theme.colors.primary.dark1,
|
||||||
base0F: theme.colors.error.dark1,
|
base0F: theme.colors.error.dark1,
|
||||||
};
|
});
|
||||||
}
|
}
|
||||||
return this.jsonTreeTheme;
|
return jsonTreeTheme;
|
||||||
}
|
};
|
||||||
|
|
||||||
getDatum(list: Datum[], index: number) {
|
const getDatum = (list: Datum[], index: number) => list[index % list.length];
|
||||||
return list[index % list.length];
|
|
||||||
}
|
|
||||||
|
|
||||||
getWidthsForColumns() {
|
const getWidthsForColumns = () => {
|
||||||
const PADDING = 50; // accounts for cell padding and width of sorting icon
|
const PADDING = 50; // accounts for cell padding and width of sorting icon
|
||||||
const widthsByColumnKey = {};
|
const widthsByColumnKey = {};
|
||||||
const cellContent = ([] as string[]).concat(
|
const cellContent = ([] as string[]).concat(
|
||||||
...this.props.orderedColumnKeys.map(key => {
|
...orderedColumnKeys.map(key => {
|
||||||
const cellContentList = this.list.map((data: Datum) =>
|
const cellContentList = list.map((data: Datum) =>
|
||||||
this.getCellContent({ cellData: data[key], columnKey: key }),
|
getCellContent({ cellData: data[key], columnKey: key }),
|
||||||
);
|
);
|
||||||
cellContentList.push(key);
|
cellContentList.push(key);
|
||||||
return cellContentList;
|
return cellContentList;
|
||||||
@ -323,30 +287,26 @@ class FilterableTable extends PureComponent<
|
|||||||
texts: cellContent,
|
texts: cellContent,
|
||||||
}).map(dimension => dimension.width);
|
}).map(dimension => dimension.width);
|
||||||
|
|
||||||
this.props.orderedColumnKeys.forEach((key, index) => {
|
orderedColumnKeys.forEach((key, index) => {
|
||||||
// we can't use Math.max(...colWidths.slice(...)) here since the number
|
// we can't use Math.max(...colWidths.slice(...)) here since the number
|
||||||
// of elements might be bigger than the number of allowed arguments in a
|
// of elements might be bigger than the number of allowed arguments in a
|
||||||
// Javascript function
|
// JavaScript function
|
||||||
const value = (widthsByColumnKey[key] =
|
widthsByColumnKey[key] =
|
||||||
colWidths
|
colWidths
|
||||||
.slice(
|
.slice(index * (list.length + 1), (index + 1) * (list.length + 1))
|
||||||
index * (this.list.length + 1),
|
.reduce((a, b) => Math.max(a, b)) + PADDING;
|
||||||
(index + 1) * (this.list.length + 1),
|
|
||||||
)
|
|
||||||
.reduce((a, b) => Math.max(a, b)) + PADDING);
|
|
||||||
widthsByColumnKey[key] = value;
|
|
||||||
});
|
});
|
||||||
|
|
||||||
return widthsByColumnKey;
|
return widthsByColumnKey;
|
||||||
}
|
};
|
||||||
|
|
||||||
getCellContent({
|
const getCellContent = ({
|
||||||
cellData,
|
cellData,
|
||||||
columnKey,
|
columnKey,
|
||||||
}: {
|
}: {
|
||||||
cellData: CellDataType;
|
cellData: CellDataType;
|
||||||
columnKey: string;
|
columnKey: string;
|
||||||
}) {
|
}) => {
|
||||||
if (cellData === null) {
|
if (cellData === null) {
|
||||||
return 'NULL';
|
return 'NULL';
|
||||||
}
|
}
|
||||||
@ -360,24 +320,35 @@ class FilterableTable extends PureComponent<
|
|||||||
} else {
|
} else {
|
||||||
truncated = '';
|
truncated = '';
|
||||||
}
|
}
|
||||||
return this.complexColumns[columnKey] ? truncated : content;
|
return complexColumns[columnKey] ? truncated : content;
|
||||||
}
|
};
|
||||||
|
|
||||||
formatTableData(data: Record<string, unknown>[]): Datum[] {
|
const [widthsForColumnsByKey] = useState<Record<string, number>>(() =>
|
||||||
return data.map(row => {
|
getWidthsForColumns(),
|
||||||
const newRow = {};
|
);
|
||||||
Object.entries(row).forEach(([key, val]) => {
|
|
||||||
if (['string', 'number'].indexOf(typeof val) >= 0) {
|
|
||||||
newRow[key] = val;
|
|
||||||
} else {
|
|
||||||
newRow[key] = val === null ? null : JSONbig.stringify(val);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
return newRow;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
hasMatch(text: string, row: Datum) {
|
const totalTableWidth = useRef(
|
||||||
|
orderedColumnKeys
|
||||||
|
.map(key => widthsForColumnsByKey[key])
|
||||||
|
.reduce((curr, next) => curr + next),
|
||||||
|
);
|
||||||
|
const totalTableHeight = useRef(height);
|
||||||
|
const container = useRef<HTMLDivElement>(null);
|
||||||
|
|
||||||
|
const fitTableToWidthIfNeeded = () => {
|
||||||
|
const containerWidth = container.current?.clientWidth ?? 0;
|
||||||
|
if (totalTableWidth.current < containerWidth) {
|
||||||
|
// fit table width if content doesn't fill the width of the container
|
||||||
|
totalTableWidth.current = containerWidth;
|
||||||
|
}
|
||||||
|
setFitted(true);
|
||||||
|
};
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
fitTableToWidthIfNeeded();
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const hasMatch = (text: string, row: Datum) => {
|
||||||
const values: string[] = [];
|
const values: string[] = [];
|
||||||
Object.keys(row).forEach(key => {
|
Object.keys(row).forEach(key => {
|
||||||
if (row.hasOwnProperty(key)) {
|
if (row.hasOwnProperty(key)) {
|
||||||
@ -394,82 +365,60 @@ class FilterableTable extends PureComponent<
|
|||||||
});
|
});
|
||||||
const lowerCaseText = text.toLowerCase();
|
const lowerCaseText = text.toLowerCase();
|
||||||
return values.some(v => v.includes(lowerCaseText));
|
return values.some(v => v.includes(lowerCaseText));
|
||||||
}
|
};
|
||||||
|
|
||||||
rowClassName({ index }: { index: number }) {
|
const rowClassName = ({ index }: { index: number }) => {
|
||||||
let className = '';
|
let className = '';
|
||||||
if (this.props.striped) {
|
if (striped) {
|
||||||
className = index % 2 === 0 ? 'even-row' : 'odd-row';
|
className = index % 2 === 0 ? 'even-row' : 'odd-row';
|
||||||
}
|
}
|
||||||
return className;
|
return className;
|
||||||
}
|
};
|
||||||
|
|
||||||
sort({
|
const sort = ({
|
||||||
sortBy,
|
sortBy,
|
||||||
sortDirection,
|
sortDirection,
|
||||||
}: {
|
}: {
|
||||||
sortBy: string;
|
sortBy: string;
|
||||||
sortDirection: SortDirectionType;
|
sortDirection: SortDirectionType;
|
||||||
}) {
|
}) => {
|
||||||
let updatedState: FilterableTableState;
|
|
||||||
|
|
||||||
const shouldClearSort =
|
const shouldClearSort =
|
||||||
this.state.sortDirection === SortDirection.DESC &&
|
sortDirectionState === SortDirection.DESC && sortByState === sortBy;
|
||||||
this.state.sortBy === sortBy;
|
|
||||||
|
|
||||||
if (shouldClearSort) {
|
if (shouldClearSort) {
|
||||||
updatedState = {
|
setSortByState(undefined);
|
||||||
...this.state,
|
setSortDirectionState(undefined);
|
||||||
sortBy: undefined,
|
setDisplayedList([...list]);
|
||||||
sortDirection: undefined,
|
|
||||||
displayedList: [...this.list],
|
|
||||||
};
|
|
||||||
} else {
|
} else {
|
||||||
updatedState = {
|
setSortByState(sortBy);
|
||||||
...this.state,
|
setSortDirectionState(sortDirection);
|
||||||
sortBy,
|
setDisplayedList(
|
||||||
sortDirection,
|
[...list].sort(
|
||||||
displayedList: [...this.list].sort(
|
sortResults(sortBy, sortDirection === SortDirection.DESC),
|
||||||
this.sortResults(sortBy, sortDirection === SortDirection.DESC),
|
|
||||||
),
|
),
|
||||||
};
|
);
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
|
||||||
this.setState(updatedState);
|
const addJsonModal = (
|
||||||
}
|
|
||||||
|
|
||||||
fitTableToWidthIfNeeded() {
|
|
||||||
const containerWidth = this.container.current?.clientWidth ?? 0;
|
|
||||||
if (this.totalTableWidth < containerWidth) {
|
|
||||||
// fit table width if content doesn't fill the width of the container
|
|
||||||
this.totalTableWidth = containerWidth;
|
|
||||||
}
|
|
||||||
this.setState({ fitted: true });
|
|
||||||
}
|
|
||||||
|
|
||||||
addJsonModal(
|
|
||||||
node: React.ReactNode,
|
node: React.ReactNode,
|
||||||
jsonObject: Record<string, unknown> | unknown[],
|
jsonObject: Record<string, unknown> | unknown[],
|
||||||
jsonString: CellDataType,
|
jsonString: CellDataType,
|
||||||
) {
|
) => (
|
||||||
return (
|
<ModalTrigger
|
||||||
<ModalTrigger
|
modalBody={<JSONTree data={jsonObject} theme={getJsonTreeTheme()} />}
|
||||||
modalBody={
|
modalFooter={
|
||||||
<JSONTree data={jsonObject} theme={this.getJsonTreeTheme()} />
|
<Button>
|
||||||
}
|
<CopyToClipboard shouldShowText={false} text={jsonString} />
|
||||||
modalFooter={
|
</Button>
|
||||||
<Button>
|
}
|
||||||
<CopyToClipboard shouldShowText={false} text={jsonString} />
|
modalTitle={t('Cell content')}
|
||||||
</Button>
|
triggerNode={node}
|
||||||
}
|
/>
|
||||||
modalTitle={t('Cell content')}
|
);
|
||||||
triggerNode={node}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Parse any numbers from strings so they'll sort correctly
|
// Parse any numbers from strings so they'll sort correctly
|
||||||
parseNumberFromString = (value: string | number | null) => {
|
const parseNumberFromString = (value: string | number | null) => {
|
||||||
if (typeof value === 'string') {
|
if (typeof value === 'string') {
|
||||||
if (ONLY_NUMBER_REGEX.test(value)) {
|
if (ONLY_NUMBER_REGEX.test(value)) {
|
||||||
return parseFloat(value);
|
return parseFloat(value);
|
||||||
@ -479,10 +428,10 @@ class FilterableTable extends PureComponent<
|
|||||||
return value;
|
return value;
|
||||||
};
|
};
|
||||||
|
|
||||||
sortResults(sortBy: string, descending: boolean) {
|
const sortResults =
|
||||||
return (a: Datum, b: Datum) => {
|
(sortBy: string, descending: boolean) => (a: Datum, b: Datum) => {
|
||||||
const aValue = this.parseNumberFromString(a[sortBy]);
|
const aValue = parseNumberFromString(a[sortBy]);
|
||||||
const bValue = this.parseNumberFromString(b[sortBy]);
|
const bValue = parseNumberFromString(b[sortBy]);
|
||||||
|
|
||||||
// equal items sort equally
|
// equal items sort equally
|
||||||
if (aValue === bValue) {
|
if (aValue === bValue) {
|
||||||
@ -502,20 +451,18 @@ class FilterableTable extends PureComponent<
|
|||||||
}
|
}
|
||||||
return aValue < bValue ? -1 : 1;
|
return aValue < bValue ? -1 : 1;
|
||||||
};
|
};
|
||||||
}
|
|
||||||
|
|
||||||
sortGrid = (label: string) => {
|
const sortGrid = (label: string) => {
|
||||||
this.sort({
|
sort({
|
||||||
sortBy: label,
|
sortBy: label,
|
||||||
sortDirection:
|
sortDirection:
|
||||||
this.state.sortDirection === SortDirection.DESC ||
|
sortDirectionState === SortDirection.DESC || sortByState !== label
|
||||||
this.state.sortBy !== label
|
|
||||||
? SortDirection.ASC
|
? SortDirection.ASC
|
||||||
: SortDirection.DESC,
|
: SortDirection.DESC,
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
renderTableHeader({
|
const renderTableHeader = ({
|
||||||
dataKey,
|
dataKey,
|
||||||
label,
|
label,
|
||||||
sortBy,
|
sortBy,
|
||||||
@ -525,9 +472,9 @@ class FilterableTable extends PureComponent<
|
|||||||
label: string;
|
label: string;
|
||||||
sortBy: string;
|
sortBy: string;
|
||||||
sortDirection: SortDirectionType;
|
sortDirection: SortDirectionType;
|
||||||
}) {
|
}) => {
|
||||||
const className =
|
const className =
|
||||||
this.props.expandedColumns.indexOf(label) > -1
|
expandedColumns.indexOf(label) > -1
|
||||||
? 'header-style-disabled'
|
? 'header-style-disabled'
|
||||||
: 'header-style';
|
: 'header-style';
|
||||||
|
|
||||||
@ -537,9 +484,9 @@ class FilterableTable extends PureComponent<
|
|||||||
{sortBy === dataKey && <SortIndicator sortDirection={sortDirection} />}
|
{sortBy === dataKey && <SortIndicator sortDirection={sortDirection} />}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
};
|
||||||
|
|
||||||
renderGridCellHeader({
|
const renderGridCellHeader = ({
|
||||||
columnIndex,
|
columnIndex,
|
||||||
key,
|
key,
|
||||||
style,
|
style,
|
||||||
@ -547,10 +494,10 @@ class FilterableTable extends PureComponent<
|
|||||||
columnIndex: number;
|
columnIndex: number;
|
||||||
key: string;
|
key: string;
|
||||||
style: React.CSSProperties;
|
style: React.CSSProperties;
|
||||||
}) {
|
}) => {
|
||||||
const label = this.props.orderedColumnKeys[columnIndex];
|
const label = orderedColumnKeys[columnIndex];
|
||||||
const className =
|
const className =
|
||||||
this.props.expandedColumns.indexOf(label) > -1
|
expandedColumns.indexOf(label) > -1
|
||||||
? 'header-style-disabled'
|
? 'header-style-disabled'
|
||||||
: 'header-style';
|
: 'header-style';
|
||||||
return (
|
return (
|
||||||
@ -566,17 +513,17 @@ class FilterableTable extends PureComponent<
|
|||||||
className={`${className} grid-cell grid-header-cell`}
|
className={`${className} grid-cell grid-header-cell`}
|
||||||
role="columnheader"
|
role="columnheader"
|
||||||
tabIndex={columnIndex}
|
tabIndex={columnIndex}
|
||||||
onClick={() => this.sortGrid(label)}
|
onClick={() => sortGrid(label)}
|
||||||
>
|
>
|
||||||
{label}
|
{label}
|
||||||
{this.state.sortBy === label && (
|
{sortByState === label && (
|
||||||
<SortIndicator sortDirection={this.state.sortDirection} />
|
<SortIndicator sortDirection={sortDirectionState} />
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
};
|
||||||
|
|
||||||
renderGridCell({
|
const renderGridCell = ({
|
||||||
columnIndex,
|
columnIndex,
|
||||||
key,
|
key,
|
||||||
rowIndex,
|
rowIndex,
|
||||||
@ -586,10 +533,10 @@ class FilterableTable extends PureComponent<
|
|||||||
key: string;
|
key: string;
|
||||||
rowIndex: number;
|
rowIndex: number;
|
||||||
style: React.CSSProperties;
|
style: React.CSSProperties;
|
||||||
}) {
|
}) => {
|
||||||
const columnKey = this.props.orderedColumnKeys[columnIndex];
|
const columnKey = orderedColumnKeys[columnIndex];
|
||||||
const cellData = this.state.displayedList[rowIndex][columnKey];
|
const cellData = displayedList[rowIndex][columnKey];
|
||||||
const cellText = this.getCellContent({ cellData, columnKey });
|
const cellText = getCellContent({ cellData, columnKey });
|
||||||
const content =
|
const content =
|
||||||
cellData === null ? <i className="text-muted">{cellText}</i> : cellText;
|
cellData === null ? <i className="text-muted">{cellText}</i> : cellText;
|
||||||
const cellNode = (
|
const cellNode = (
|
||||||
@ -602,7 +549,7 @@ class FilterableTable extends PureComponent<
|
|||||||
? style.top - GRID_POSITION_ADJUSTMENT
|
? style.top - GRID_POSITION_ADJUSTMENT
|
||||||
: style.top,
|
: style.top,
|
||||||
}}
|
}}
|
||||||
className={`grid-cell ${this.rowClassName({ index: rowIndex })}`}
|
className={`grid-cell ${rowClassName({ index: rowIndex })}`}
|
||||||
>
|
>
|
||||||
<div css={{ width: 'inherit' }}>{content}</div>
|
<div css={{ width: 'inherit' }}>{content}</div>
|
||||||
</div>
|
</div>
|
||||||
@ -610,33 +557,23 @@ class FilterableTable extends PureComponent<
|
|||||||
|
|
||||||
const jsonObject = safeJsonObjectParse(cellData);
|
const jsonObject = safeJsonObjectParse(cellData);
|
||||||
if (jsonObject) {
|
if (jsonObject) {
|
||||||
return this.addJsonModal(cellNode, jsonObject, cellData);
|
return addJsonModal(cellNode, jsonObject, cellData);
|
||||||
}
|
}
|
||||||
return cellNode;
|
return cellNode;
|
||||||
}
|
};
|
||||||
|
|
||||||
renderGrid() {
|
const renderGrid = () => {
|
||||||
const {
|
|
||||||
orderedColumnKeys,
|
|
||||||
overscanColumnCount,
|
|
||||||
overscanRowCount,
|
|
||||||
rowHeight,
|
|
||||||
} = this.props;
|
|
||||||
|
|
||||||
let { height } = this.props;
|
|
||||||
let totalTableHeight = height;
|
|
||||||
if (
|
if (
|
||||||
this.container.current &&
|
container.current &&
|
||||||
this.totalTableWidth > this.container.current.clientWidth
|
totalTableWidth.current > container.current.clientWidth
|
||||||
) {
|
) {
|
||||||
// exclude the height of the horizontal scroll bar from the height of the table
|
// exclude the height of the horizontal scroll bar from the height of the table
|
||||||
// and the height of the table container if the content overflows
|
// and the height of the table container if the content overflows
|
||||||
height -= SCROLL_BAR_HEIGHT;
|
totalTableHeight.current -= SCROLL_BAR_HEIGHT;
|
||||||
totalTableHeight -= SCROLL_BAR_HEIGHT;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const getColumnWidth = ({ index }: { index: number }) =>
|
const getColumnWidth = ({ index }: { index: number }) =>
|
||||||
this.widthsForColumnsByKey[orderedColumnKeys[index]];
|
widthsForColumnsByKey[orderedColumnKeys[index]];
|
||||||
|
|
||||||
// fix height of filterable table
|
// fix height of filterable table
|
||||||
return (
|
return (
|
||||||
@ -648,7 +585,7 @@ class FilterableTable extends PureComponent<
|
|||||||
{({ width }) => (
|
{({ width }) => (
|
||||||
<div>
|
<div>
|
||||||
<Grid
|
<Grid
|
||||||
cellRenderer={this.renderGridCellHeader}
|
cellRenderer={renderGridCellHeader}
|
||||||
columnCount={orderedColumnKeys.length}
|
columnCount={orderedColumnKeys.length}
|
||||||
columnWidth={getColumnWidth}
|
columnWidth={getColumnWidth}
|
||||||
height={rowHeight}
|
height={rowHeight}
|
||||||
@ -659,14 +596,14 @@ class FilterableTable extends PureComponent<
|
|||||||
style={{ overflow: 'hidden' }}
|
style={{ overflow: 'hidden' }}
|
||||||
/>
|
/>
|
||||||
<Grid
|
<Grid
|
||||||
cellRenderer={this.renderGridCell}
|
cellRenderer={renderGridCell}
|
||||||
columnCount={orderedColumnKeys.length}
|
columnCount={orderedColumnKeys.length}
|
||||||
columnWidth={getColumnWidth}
|
columnWidth={getColumnWidth}
|
||||||
height={totalTableHeight - rowHeight}
|
height={totalTableHeight.current - rowHeight}
|
||||||
onScroll={onScroll}
|
onScroll={onScroll}
|
||||||
overscanColumnCount={overscanColumnCount}
|
overscanColumnCount={overscanColumnCount}
|
||||||
overscanRowCount={overscanRowCount}
|
overscanRowCount={overscanRowCount}
|
||||||
rowCount={this.list.length}
|
rowCount={list.length}
|
||||||
rowHeight={rowHeight}
|
rowHeight={rowHeight}
|
||||||
width={width}
|
width={width}
|
||||||
/>
|
/>
|
||||||
@ -678,86 +615,73 @@ class FilterableTable extends PureComponent<
|
|||||||
</ScrollSync>
|
</ScrollSync>
|
||||||
</StyledFilterableTable>
|
</StyledFilterableTable>
|
||||||
);
|
);
|
||||||
}
|
};
|
||||||
|
|
||||||
renderTableCell({
|
const renderTableCell = ({
|
||||||
cellData,
|
cellData,
|
||||||
columnKey,
|
columnKey,
|
||||||
}: {
|
}: {
|
||||||
cellData: CellDataType;
|
cellData: CellDataType;
|
||||||
columnKey: string;
|
columnKey: string;
|
||||||
}) {
|
}) => {
|
||||||
const cellNode = this.getCellContent({ cellData, columnKey });
|
const cellNode = getCellContent({ cellData, columnKey });
|
||||||
const content =
|
const content =
|
||||||
cellData === null ? <i className="text-muted">{cellNode}</i> : cellNode;
|
cellData === null ? <i className="text-muted">{cellNode}</i> : cellNode;
|
||||||
const jsonObject = safeJsonObjectParse(cellData);
|
const jsonObject = safeJsonObjectParse(cellData);
|
||||||
if (jsonObject) {
|
if (jsonObject) {
|
||||||
return this.addJsonModal(cellNode, jsonObject, cellData);
|
return addJsonModal(cellNode, jsonObject, cellData);
|
||||||
}
|
}
|
||||||
return content;
|
return content;
|
||||||
}
|
};
|
||||||
|
|
||||||
renderTable() {
|
const renderTable = () => {
|
||||||
const { sortBy, sortDirection } = this.state;
|
let sortedAndFilteredList = displayedList;
|
||||||
const {
|
|
||||||
filterText,
|
|
||||||
headerHeight,
|
|
||||||
orderedColumnKeys,
|
|
||||||
overscanRowCount,
|
|
||||||
rowHeight,
|
|
||||||
} = this.props;
|
|
||||||
|
|
||||||
let sortedAndFilteredList = this.state.displayedList;
|
|
||||||
// filter list
|
// filter list
|
||||||
if (filterText) {
|
if (filterText) {
|
||||||
sortedAndFilteredList = sortedAndFilteredList.filter((row: Datum) =>
|
sortedAndFilteredList = sortedAndFilteredList.filter((row: Datum) =>
|
||||||
this.hasMatch(filterText, row),
|
hasMatch(filterText, row),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
let { height } = this.props;
|
|
||||||
let totalTableHeight = height;
|
|
||||||
if (
|
if (
|
||||||
this.container.current &&
|
container.current &&
|
||||||
this.totalTableWidth > this.container.current.clientWidth
|
totalTableWidth.current > container.current.clientWidth
|
||||||
) {
|
) {
|
||||||
// exclude the height of the horizontal scroll bar from the height of the table
|
// exclude the height of the horizontal scroll bar from the height of the table
|
||||||
// and the height of the table container if the content overflows
|
// and the height of the table container if the content overflows
|
||||||
height -= SCROLL_BAR_HEIGHT;
|
totalTableHeight.current -= SCROLL_BAR_HEIGHT;
|
||||||
totalTableHeight -= SCROLL_BAR_HEIGHT;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const rowGetter = ({ index }: { index: number }) =>
|
const rowGetter = ({ index }: { index: number }) =>
|
||||||
this.getDatum(sortedAndFilteredList, index);
|
getDatum(sortedAndFilteredList, index);
|
||||||
return (
|
return (
|
||||||
<StyledFilterableTable
|
<StyledFilterableTable
|
||||||
className="filterable-table-container"
|
className="filterable-table-container"
|
||||||
ref={this.container}
|
ref={container}
|
||||||
>
|
>
|
||||||
{this.state.fitted && (
|
{fitted && (
|
||||||
<Table
|
<Table
|
||||||
ref="Table"
|
|
||||||
headerHeight={headerHeight}
|
headerHeight={headerHeight}
|
||||||
height={totalTableHeight}
|
height={totalTableHeight.current}
|
||||||
overscanRowCount={overscanRowCount}
|
overscanRowCount={overscanRowCount}
|
||||||
rowClassName={this.rowClassName}
|
rowClassName={rowClassName}
|
||||||
rowHeight={rowHeight}
|
rowHeight={rowHeight}
|
||||||
rowGetter={rowGetter}
|
rowGetter={rowGetter}
|
||||||
rowCount={sortedAndFilteredList.length}
|
rowCount={sortedAndFilteredList.length}
|
||||||
sort={this.sort}
|
sort={sort}
|
||||||
sortBy={sortBy}
|
sortBy={sortByState}
|
||||||
sortDirection={sortDirection}
|
sortDirection={sortDirectionState}
|
||||||
width={this.totalTableWidth}
|
width={totalTableWidth.current}
|
||||||
>
|
>
|
||||||
{orderedColumnKeys.map(columnKey => (
|
{orderedColumnKeys.map(columnKey => (
|
||||||
<Column
|
<Column
|
||||||
cellRenderer={({ cellData }) =>
|
cellRenderer={({ cellData }) =>
|
||||||
this.renderTableCell({ cellData, columnKey })
|
renderTableCell({ cellData, columnKey })
|
||||||
}
|
}
|
||||||
dataKey={columnKey}
|
dataKey={columnKey}
|
||||||
disableSort={false}
|
disableSort={false}
|
||||||
headerRenderer={this.renderTableHeader}
|
headerRenderer={renderTableHeader}
|
||||||
width={this.widthsForColumnsByKey[columnKey]}
|
width={widthsForColumnsByKey[columnKey]}
|
||||||
label={columnKey}
|
label={columnKey}
|
||||||
key={columnKey}
|
key={columnKey}
|
||||||
/>
|
/>
|
||||||
@ -766,14 +690,12 @@ class FilterableTable extends PureComponent<
|
|||||||
)}
|
)}
|
||||||
</StyledFilterableTable>
|
</StyledFilterableTable>
|
||||||
);
|
);
|
||||||
}
|
};
|
||||||
|
|
||||||
render() {
|
if (orderedColumnKeys.length > MAX_COLUMNS_FOR_TABLE) {
|
||||||
if (this.props.orderedColumnKeys.length > MAX_COLUMNS_FOR_TABLE) {
|
return renderGrid();
|
||||||
return this.renderGrid();
|
|
||||||
}
|
|
||||||
return this.renderTable();
|
|
||||||
}
|
}
|
||||||
}
|
return renderTable();
|
||||||
|
};
|
||||||
|
|
||||||
export default withTheme(FilterableTable);
|
export default FilterableTable;
|
||||||
|
Loading…
Reference in New Issue
Block a user