Skip to content

Commit

Permalink
feat: data bar render from API (#1415)
Browse files Browse the repository at this point in the history
No specific ticket to close. Just alters the front-end to allow the new
data from JSON API to be parsed and turned into a data bar

Works in tandem with this
deephaven/deephaven-core#4181 from
deephaven-core

### Demo Code:
```
from deephaven import new_table
from deephaven.column import int_col, double_col, string_col

students = new_table([
    string_col("Name", ["Andy", "Claire", "Jane", "Steven"]),
    int_col("StudentID", [1, 2, 3, 4]),
    int_col("Threshold", [3, 3, 3, 3]),
    double_col("GPA", [-3.0, 4.0, 3.7, 2.8]),
    double_col("DirectionalAxis", [-3.0, 4.0, 3.7, 2.8]),
    double_col("MiddleAxis", [-3.0, 4.0, 3.7, 2.8]),
    double_col("OverlapPlacement", [-3.0, 4.0, 3.7, 2.8]),
    double_col("HidePlacement", [-3.0, 4.0, 3.7, 2.8]),
    double_col("RTLDirection", [-3.0, 4.0, 3.7, 2.8]),
    double_col("Markers", [-3.0, 4.0, 3.7, 2.8]),
    double_col("Markers2", [-3.0, 4.0, 3.7, 2.8]),
    double_col("Gradient", [-3.0, 4.0, 3.7, 2.8]),
])

data_bars = students.format_data_bar(column= "GPA", value_column= "GPA").format_data_bar(column="DirectionalAxis", value_column="DirectionalAxis", axis="directional").format_data_bar(column="MiddleAxis", value_column="MiddleAxis", axis="middle").format_data_bar(column="OverlapPlacement", value_column="OverlapPlacement", value_placement="overlap").format_data_bar(column="HidePlacement", value_column="HidePlacement", value_placement="hide").format_data_bar(column="RTLDirection", value_column="RTLDirection", direction="RTL").format_data_bar(column="Markers", value_column="Markers", marker_column="Threshold").format_data_bar(column="Markers2", value_column="Markers2", marker_column="StudentID", marker_color="#0000FF").format_data_bar(column="Gradient", value_column="Gradient", negative_color=["#FF0000", "#FFFF00"], positive_color=["#FFFF00", "#00FF00"])
```

### Expected Output:
<img width="1728" alt="image"
src="https://github.com/deephaven/deephaven-core/assets/55671206/004372e1-dee1-4632-97f9-73d2e0bb9a98">
  • Loading branch information
ethanalvizo authored Oct 4, 2023
1 parent ce51229 commit ee7d1c1
Show file tree
Hide file tree
Showing 11 changed files with 125 additions and 27 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -54,8 +54,8 @@ function DataBarExample(): JSX.Element {
[
12,
[
{ column: 13, color: 'white' },
{ column: 14, color: 'gray' },
{ value: 13, color: 'white' },
{ value: 14, color: 'gray' },
],
],
]);
Expand Down
31 changes: 14 additions & 17 deletions packages/grid/src/DataBarCellRenderer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ class DataBarCellRenderer extends CellRenderer {
markers,
direction,
value,
} = model.dataBarOptionsForCell(modelColumn, modelRow);
} = model.dataBarOptionsForCell(modelColumn, modelRow, theme);

const hasGradient = Array.isArray(dataBarColor);
if (columnMin == null || columnMax == null) {
Expand Down Expand Up @@ -343,7 +343,7 @@ class DataBarCellRenderer extends CellRenderer {
markers,
direction,
value,
} = model.dataBarOptionsForCell(modelColumn, modelRow);
} = model.dataBarOptionsForCell(modelColumn, modelRow, theme);
const longestValueWidth = this.getCachedWidestValueForColumn(
context,
visibleRows,
Expand Down Expand Up @@ -398,11 +398,9 @@ class DataBarCellRenderer extends CellRenderer {
? zeroPosition
: zeroPosition - (Math.abs(value) / totalValueRange) * maxWidth;
let markerXs = markers.map(marker => {
const { column: markerColumn } = marker;
const markerValue = Number(model.textForCell(markerColumn, modelRow));
return markerValue >= 0
? zeroPosition + (Math.abs(markerValue) / totalValueRange) * maxWidth
: zeroPosition - (Math.abs(markerValue) / totalValueRange) * maxWidth;
const { value: markerValue } = marker;
const offset = (Math.abs(markerValue) / totalValueRange) * maxWidth;
return markerValue >= 0 ? zeroPosition + offset : zeroPosition - offset;
});
let leftmostPosition =
valuePlacement === 'beside' && textAlign === 'left'
Expand All @@ -424,8 +422,7 @@ class DataBarCellRenderer extends CellRenderer {
? zeroPosition - (value / totalValueRange) * maxWidth
: zeroPosition;
markerXs = markers.map(marker => {
const { column: markerColumn } = marker;
const markerValue = Number(model.textForCell(markerColumn, modelRow));
const { value: markerValue } = marker;
return markerValue >= 0
? zeroPosition - (Math.abs(markerValue) / totalValueRange) * maxWidth
: zeroPosition + (Math.abs(markerValue) / totalValueRange) * maxWidth;
Expand All @@ -441,8 +438,8 @@ class DataBarCellRenderer extends CellRenderer {
? zeroPosition
: zeroPosition - (Math.abs(value) / columnLongest) * (maxWidth / 2);
markerXs = markers.map(marker => {
const { column: markerColumn } = marker;
const markerValue = Number(model.textForCell(markerColumn, modelRow));
const { value: markerValue } = marker;

return markerValue >= 0
? zeroPosition +
(Math.abs(markerValue) / columnLongest) * (maxWidth / 2)
Expand All @@ -456,8 +453,8 @@ class DataBarCellRenderer extends CellRenderer {
? zeroPosition
: zeroPosition - (Math.abs(value) / columnLongest) * (maxWidth / 2);
markerXs = markers.map(marker => {
const { column: markerColumn } = marker;
const markerValue = Number(model.textForCell(markerColumn, modelRow));
const { value: markerValue } = marker;

return markerValue <= 0
? zeroPosition +
(Math.abs(markerValue) / columnLongest) * (maxWidth / 2)
Expand All @@ -471,8 +468,8 @@ class DataBarCellRenderer extends CellRenderer {
zeroPosition = 0;
dataBarX = zeroPosition;
markerXs = markers.map(marker => {
const { column: markerColumn } = marker;
const markerValue = Number(model.textForCell(markerColumn, modelRow));
const { value: markerValue } = marker;

return (
zeroPosition + (Math.abs(markerValue) / columnLongest) * maxWidth
);
Expand All @@ -482,8 +479,8 @@ class DataBarCellRenderer extends CellRenderer {
zeroPosition = columnWidth;
dataBarX = zeroPosition - (Math.abs(value) / columnLongest) * maxWidth;
markerXs = markers.map(marker => {
const { column: markerColumn } = marker;
const markerValue = Number(model.textForCell(markerColumn, modelRow));
const { value: markerValue } = marker;

return (
zeroPosition - (Math.abs(markerValue) / columnLongest) * maxWidth
);
Expand Down
9 changes: 7 additions & 2 deletions packages/grid/src/DataBarGridModel.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import { GridThemeType } from '.';
import { ModelIndex } from './GridMetrics';
import GridModel from './GridModel';
import { GridColor } from './GridTheme';

export type Marker = { column: ModelIndex; color: string };
export type Marker = { value: number; color: string };
export type AxisOption = 'proportional' | 'middle' | 'directional';
export type ValuePlacementOption = 'beside' | 'overlap' | 'hide';
export type DirectionOption = 'LTR' | 'RTL';
Expand Down Expand Up @@ -49,5 +50,9 @@ export function isDataBarGridModel(
}

export interface DataBarGridModel extends GridModel {
dataBarOptionsForCell(column: ModelIndex, row: ModelIndex): DataBarOptions;
dataBarOptionsForCell(
column: ModelIndex,
row: ModelIndex,
theme: GridThemeType
): DataBarOptions;
}
2 changes: 2 additions & 0 deletions packages/grid/src/GridTheme.ts
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,7 @@ export type GridTheme = {
zeroLineColor: GridColor;
positiveBarColor: GridColor;
negativeBarColor: GridColor;
markerBarColor: GridColor;

dataBarHorizontalPadding: number;
};
Expand Down Expand Up @@ -232,6 +233,7 @@ const defaultTheme: GridTheme = Object.freeze({
zeroLineColor: '#888888',
positiveBarColor: '#00ff00',
negativeBarColor: '#ff0000',
markerBarColor: '#ffffff',

dataBarHorizontalPadding: 90,
});
Expand Down
1 change: 1 addition & 0 deletions packages/grid/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,3 +31,4 @@ export { default as TextCellRenderer } from './TextCellRenderer';
export { default as DataBarCellRenderer } from './DataBarCellRenderer';
export * from './TokenBoxCellRenderer';
export * from './GridRendererTypes';
export * from './DataBarGridModel';
26 changes: 20 additions & 6 deletions packages/iris-grid/src/IrisGridModel.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
/* eslint-disable class-methods-use-this */
import type { Event, EventTarget } from 'event-target-shim';
import {
DataBarGridModel,
DataBarOptions,
GridModel,
GridRange,
ModelIndex,
Expand Down Expand Up @@ -29,6 +31,7 @@ import {
PendingDataErrorMap,
} from './CommonTypes';
import ColumnHeaderGroup from './ColumnHeaderGroup';
import { IrisGridThemeType } from './IrisGridTheme';

type IrisGridModelEventNames =
(typeof IrisGridModel.EVENT)[keyof typeof IrisGridModel.EVENT];
Expand All @@ -47,12 +50,15 @@ const EMPTY_ARRAY: never[] = [];
* those out as well, so there's no dependency on IrisAPI at all, but it's a lot of work for no real gain at this time.
*/
abstract class IrisGridModel<
TEventMap extends Record<string, Event<string>> = Record<
string,
Event<string>
>,
TMode extends 'standard' | 'strict' = 'standard',
> extends GridModel<TEventMap & IrisGridModelEventMap, TMode> {
TEventMap extends Record<string, Event<string>> = Record<
string,
Event<string>
>,
TMode extends 'standard' | 'strict' = 'standard',
>
extends GridModel<TEventMap & IrisGridModelEventMap, TMode>
implements DataBarGridModel
{
static EVENT = Object.freeze({
UPDATED: 'UPDATED',
FORMATTER_UPDATED: 'FORMATTER_UPDATED',
Expand Down Expand Up @@ -532,6 +538,14 @@ abstract class IrisGridModel<
modelIndex: ModelIndex,
depth: number
): ColumnHeaderGroup | undefined;

dataBarOptionsForCell(
column: number,
row: number,
theme: IrisGridThemeType
): DataBarOptions {
throw new Error('Method not implemented.');
}
}

export default IrisGridModel;
6 changes: 6 additions & 0 deletions packages/iris-grid/src/IrisGridProxyModel.ts
Original file line number Diff line number Diff line change
Expand Up @@ -462,6 +462,12 @@ class IrisGridProxyModel extends IrisGridModel {
valueForCell: IrisGridModel['valueForCell'] = (...args) =>
this.model.valueForCell(...args);

renderTypeForCell: IrisGridModel['renderTypeForCell'] = (...args) =>
this.model.renderTypeForCell(...args);

dataBarOptionsForCell: IrisGridModel['dataBarOptionsForCell'] = (...args) =>
this.model.dataBarOptionsForCell(...args);

get filter(): readonly FilterCondition[] {
return this.model.filter;
}
Expand Down
56 changes: 56 additions & 0 deletions packages/iris-grid/src/IrisGridTableModelTemplate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import memoize from 'memoize-one';
import throttle from 'lodash.throttle';
import {
CellRenderType,
EditOperation,
GridRange,
GridUtils,
Expand Down Expand Up @@ -38,6 +39,13 @@ import {
assertNotNull,
} from '@deephaven/utils';
import { TableUtils, Formatter, FormatterUtils } from '@deephaven/jsapi-utils';
import {
AxisOption,
DataBarOptions,
DirectionOption,
Marker,
ValuePlacementOption,
} from 'packages/grid/src/DataBarGridModel';
import IrisGridModel from './IrisGridModel';
import AggregationOperation from './sidebar/aggregations/AggregationOperation';
import IrisGridUtils from './IrisGridUtils';
Expand Down Expand Up @@ -635,6 +643,54 @@ class IrisGridTableModelTemplate<
return 'left';
}

dataBarOptionsForCell(
column: ModelIndex,
row: ModelIndex,
theme: IrisGridThemeType
): DataBarOptions {
const format = this.formatForCell(column, row);
assertNotNull(format);
const { axis, direction, max, min, valuePlacement, value, marker } =
format.formatDataBar;
let { positiveColor, negativeColor, markerColor, opacity } =
format.formatDataBar;

positiveColor = positiveColor ?? theme.positiveBarColor;
negativeColor = negativeColor ?? theme.negativeBarColor;
let databarColor: string | string[] =
format.color ?? (value >= 0 ? positiveColor : negativeColor);
if (databarColor.includes(',')) {
databarColor = databarColor.split(',');
}
markerColor = markerColor ?? theme.markerBarColor;

opacity = valuePlacement.toLowerCase() === 'overlap' ? 0.5 : opacity;

const databarOptions = {
axis: axis.toLowerCase() as AxisOption,
direction: direction.toUpperCase() as DirectionOption,
columnMax: max,
columnMin: min,
opacity,
color: databarColor,
valuePlacement: valuePlacement.toLowerCase() as ValuePlacementOption,
value,
markers: [
{
value: marker,
color: markerColor,
},
] as Marker[],
};

return databarOptions;
}

renderTypeForCell(column: ModelIndex, row: ModelIndex): CellRenderType {
const format = this.formatForCell(column, row);
return format?.formatDataBar != null ? 'dataBar' : 'text';
}

textForColumnHeader(x: ModelIndex, depth = 0): string | undefined {
const header = this.columnAtDepth(x, depth);
if (isColumnHeaderGroup(header)) {
Expand Down
1 change: 1 addition & 0 deletions packages/iris-grid/src/IrisGridTheme.module.scss
Original file line number Diff line number Diff line change
Expand Up @@ -99,4 +99,5 @@ $header-height: 30px;
zero-line-color: $gray-500;
positive-bar-color: $green;
negative-bar-color: $red;
marker-bar-color: $white;
}
1 change: 1 addition & 0 deletions packages/iris-grid/src/IrisGridTheme.ts
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,7 @@ const theme: Partial<IrisGridThemeType> = Object.freeze({
zeroLineColor: IrisGridTheme['zero-line-color'],
positiveBarColor: IrisGridTheme['positive-bar-color'],
negativeBarColor: IrisGridTheme['negative-bar-color'],
markerBarColor: IrisGridTheme['marker-bar-color'],
});

export default theme;
15 changes: 15 additions & 0 deletions packages/jsapi-types/src/dh.types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -853,6 +853,21 @@ export interface Format {
readonly color: string;
readonly backgroundColor: string;
readonly formatString: string;
readonly formatDataBar: DatabarFormat;
}

export interface DatabarFormat {
axis: string;
direction: string;
max: number;
min: number;
negativeColor: string | string[];
opacity: number;
positiveColor: string | string[];
valuePlacement: string;
value: number;
marker: number;
markerColor: string | string[];
}

export interface ColumnStatistics {
Expand Down

0 comments on commit ee7d1c1

Please sign in to comment.