Skip to content

Commit

Permalink
[Feat] add table config with custom number format (#2192)
Browse files Browse the repository at this point in the history
  • Loading branch information
igorDykhta committed Apr 12, 2023
1 parent e635e4c commit e9896de
Show file tree
Hide file tree
Showing 23 changed files with 467 additions and 100 deletions.
1 change: 1 addition & 0 deletions src/actions/src/action-types.ts
Expand Up @@ -112,6 +112,7 @@ export const ActionTypes = {
SORT_TABLE_COLUMN: `${ACTION_PREFIX}SORT_TABLE_COLUMN`,
PIN_TABLE_COLUMN: `${ACTION_PREFIX}PIN_TABLE_COLUMN`,
COPY_TABLE_COLUMN: `${ACTION_PREFIX}COPY_TABLE_COLUMN`,
SET_COLUMN_DISPLAY_FORMAT: `${ACTION_PREFIX}SET_COLUMN_DISPLAY_FORMAT`,
NEXT_FILE_BATCH: `${ACTION_PREFIX}NEXT_FILE_BATCH`,
PROCESS_FILE_CONTENT: `${ACTION_PREFIX}PROCESS_FILE_CONTENT`,
UPDATE_TABLE_COLOR: `${ACTION_PREFIX}UPDATE_TABLE_COLOR`,
Expand Down
30 changes: 30 additions & 0 deletions src/actions/src/vis-state-actions.ts
Expand Up @@ -620,6 +620,36 @@ export function copyTableColumn(
};
}

export type SetColumnDisplayFormatUpdaterAction = {
dataId: string;
column: string;
displayFormat: string;
};

/**
* Set column display format
* @param dataId
* @param column
* @param displayFormat
* @returns action
* @public
*/
export function setColumnDisplayFormat(
dataId: string,
column: string,
displayFormat: string
): Merge<
SetColumnDisplayFormatUpdaterAction,
{type: typeof ActionTypes.SET_COLUMN_DISPLAY_FORMAT}
> {
return {
type: ActionTypes.SET_COLUMN_DISPLAY_FORMAT,
dataId,
column,
displayFormat
};
}

export type AddDataToMapUpdaterOptions = {
centrMap?: boolean;
readOnly?: boolean;
Expand Down
52 changes: 48 additions & 4 deletions src/components/src/common/data-table/header-cell.tsx
@@ -1,10 +1,12 @@
import React, {CSSProperties, useCallback} from 'react';
import React, {CSSProperties, useState, useCallback} from 'react';
import styled from 'styled-components';
import classnames from 'classnames';
import Button from './button';
import {ArrowUp, ArrowDown, VertThreeDots} from '../../common/icons';
import OptionDropdown from './option-dropdown';
import {ArrowUp, ArrowDown, VertThreeDots, Hash} from '../../common/icons';
import {SORT_ORDER} from '@kepler.gl/constants';
import OptionDropdown, {FormatterDropdown} from './option-dropdown';
import {getFieldFormatLabels} from '@kepler.gl/utils';
import {ColMeta} from '@kepler.gl/types';
import FieldTokenFactory, {FieldTokenProps} from '../../common/field-token';
import {DataTableProps} from './index';

Expand Down Expand Up @@ -58,6 +60,12 @@ const StyledHeaderCell = styled.div`
.more {
margin-left: 5px;
}
.col-name__format svg {
width: 10px;
height: 10px;
stroke-width: 1;
}
`;

type CellInfo = {
Expand All @@ -74,6 +82,7 @@ type HeaderCellProps = {
// passed down from react virtualized Grid
cellInfo: CellInfo;
columns: DataTableProps['columns'];
colMeta?: ColMeta;
isPinned?: boolean;
showStats?: boolean;
props: DataTableProps;
Expand All @@ -91,19 +100,40 @@ const HeaderCellFactory = (FieldToken: React.FC<FieldTokenProps>) => {
moreOptionsColumn
}: HeaderCellProps) => {
const {columnIndex, key, style} = cellInfo;
const {colMeta, sortColumn = {}, sortTableColumn, pinTableColumn, copyTableColumn} = props;
const {
colMeta,
sortColumn,
sortTableColumn,
pinTableColumn,
copyTableColumn,
setDisplayFormat
} = props;
const [showFormatter, setShowFormatter] = useState(false);
const column = columns[columnIndex];

const isGhost = column.ghost;
const isSorted = sortColumn[column];
const firstCell = columnIndex === 0;
const isFormatted = Boolean(colMeta[column]?.displayFormat);
const formatLabels = isFormatted ? getFieldFormatLabels(colMeta[column].type) : [];
const onSortTable = useCallback(() => sortTableColumn(column), [sortTableColumn, column]);
const onToggleOptionMenu = useCallback(() => toggleMoreOptions(column), [
toggleMoreOptions,
column
]);
const onPin = useCallback(() => pinTableColumn(column), [pinTableColumn, column]);
const onCopy = useCallback(() => copyTableColumn(column), [copyTableColumn, column]);
const onSetDisplayFormat = useCallback(
displayFormat => {
setDisplayFormat(column, displayFormat.format);
},
[column, setDisplayFormat]
);

const onToggleDisplayFormat = useCallback(() => {
setShowFormatter(!showFormatter);
}, [showFormatter]);

return (
<StyledHeaderCell
className={classnames('header-cell', {
Expand Down Expand Up @@ -136,6 +166,18 @@ const HeaderCellFactory = (FieldToken: React.FC<FieldTokenProps>) => {
)
) : null}
</Button>
<Button className="col-name__format" onClick={onToggleDisplayFormat}>
{isFormatted ? <Hash height="14px" /> : null}
<FormatterDropdown
left={0}
top={0}
isOpened={isFormatted && showFormatter}
column={colMeta[column]}
setDisplayFormat={onSetDisplayFormat}
onClose={() => setShowFormatter(false)}
formatLabels={formatLabels}
/>
</Button>
</div>
<Button className="more" onClick={onToggleOptionMenu}>
<VertThreeDots height="14px" />
Expand All @@ -148,10 +190,12 @@ const HeaderCellFactory = (FieldToken: React.FC<FieldTokenProps>) => {
<OptionDropdown
isOpened={moreOptionsColumn === column}
column={column}
colMeta={colMeta}
toggleMoreOptions={toggleMoreOptions}
sortTableColumn={mode => sortTableColumn(column, mode)}
pinTableColumn={onPin}
copyTableColumn={onCopy}
setDisplayFormat={onSetDisplayFormat}
/>
</section>
</>
Expand Down
34 changes: 11 additions & 23 deletions src/components/src/common/data-table/index.tsx
Expand Up @@ -32,7 +32,8 @@ import {CellSizeCache} from './cell-size';
import Grid from './grid';
import HeaderCellFactory from './header-cell';

import {parseFieldValue, DataContainerInterface} from '@kepler.gl/utils';
import {ColMeta} from '@kepler.gl/types';
import {parseFieldValue, getColumnFormatter, DataContainerInterface} from '@kepler.gl/utils';
import {adjustCellsToContainer} from './cell-size';

import {ALL_FIELD_TYPES} from '@kepler.gl/constants';
Expand Down Expand Up @@ -189,17 +190,6 @@ export const Container = styled.div`

const defaultColumnWidth = 200;

export type ColMeta = {
[key: string]: {
colIdx: number;
name: string;
displayName: string;
type: string;
format?: string;
columnStats?: any;
};
};

export type SortColumn = {
column?: string;
mode?: string;
Expand All @@ -221,20 +211,16 @@ interface GetRowCellProps {
/*
* This is an accessor method used to generalize getting a cell from a data row
*/
const getRowCell = ({
dataContainer,
columns,
column,
colMeta,
rowIndex,
sortOrder
}: GetRowCellProps) => {
const getRowCell = (
{dataContainer, columns, column, colMeta, rowIndex, sortOrder}: GetRowCellProps,
formatter
) => {
const rowIdx = sortOrder && sortOrder.length ? get(sortOrder, rowIndex) : rowIndex;
const {type} = colMeta[column];

let value = dataContainer.valueAt(rowIdx, columns.indexOf(column));
if (value === undefined) value = 'Err';
return parseFieldValue(value, type);
return formatter ? formatter(value) : parseFieldValue(value, type);
};

type StatsControlProps = {
Expand Down Expand Up @@ -401,9 +387,10 @@ export interface DataTableProps {
dataContainer: DataContainerInterface;
fixedHeight?: number;
colMeta: ColMeta;
sortColumn?: SortColumn;
sortColumn: SortColumn;
sortTableColumn: (column: string, mode?: string) => void;
pinTableColumn: (column: string) => void;
setDisplayFormat: (column: string, displayFormat: string) => void;
copyTableColumn: (column: string) => void;
sortOrder?: number[] | null;
showStats?: boolean;
Expand Down Expand Up @@ -509,7 +496,8 @@ function DataTableFactory(HeaderCell: ReturnType<typeof HeaderCellFactory>) {
const column = columns[columnIndex];
const isGhost = column.ghost;

const rowCell = isGhost ? '' : getRowCell({...props, column, rowIndex});
const formatter = isGhost ? null : getColumnFormatter(colMeta[column]);
const rowCell = isGhost ? '' : getRowCell({...props, column, rowIndex}, formatter);
const type = isGhost ? null : colMeta[column].type;

const lastRowIndex = dataContainer ? dataContainer.numRows() - 1 : 0;
Expand Down

0 comments on commit e9896de

Please sign in to comment.