Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add pivot frame to grid subcomponents #96

Merged
merged 8 commits into from Jul 16, 2019
@@ -1,6 +1,6 @@
{
"name": "@operational/frame",
"version": "0.2.0",
"version": "0.3.0",
"description": "Contiamo DataFrame.",
"main": "./lib/index.js",
"types": "./lib/index.d.ts",
@@ -1,6 +1,6 @@
{
"name": "@operational/grid",
"version": "0.2.0",
"version": "0.3.0",
"description": "Contiamo visualization library.",
"main": "./lib/index.js",
"types": "./lib/index.d.ts",
@@ -1,4 +1,4 @@
import { FragmentFrame, PivotFrame } from "@operational/frame";
import { PivotFrame } from "@operational/frame";
import React, { useCallback, useMemo } from "react";
import { GridChildComponentProps, VariableSizeGrid } from "react-window";

@@ -13,10 +13,16 @@ import {
indexToCoordinate,
} from "./coordinateUtils";

import { DimensionLabels, HeightParam, WidthParam } from "./types";

type Diff<T, U> = T extends U ? never : T;
type Defined<T> = Diff<T, undefined>;
import {
DimensionLabels,
RowProps,
ColumnProps,
CellPropsWithMeasure,
CellPropsWithoutMeasure,
MeasuresPlacement,
WidthAccessor,
HeightAccessor,
} from "./types";

// Optimisation for hooks, because {} !== {}
const emptyObject = Object.freeze({});
@@ -34,8 +40,18 @@ const toString = (value: boolean | string) => {
return value;
};

const defaultCell = <Name extends string = string>({ data, measure }: { data: FragmentFrame<Name>; measure: Name }) => {
const value = data.peak(measure);
const defaultCell = <Name extends string = string>({
column,
row,
data,
measure,
}: {
data: PivotFrame<Name>;
row: number;
column: number;
measure: Name;
}) => {
const value = data.cell(row, column).peak(measure);
return value === null ? null : <>{value}</>;
};

@@ -65,7 +81,7 @@ interface TextOnlyPivotGridProps<Name extends string> {
type?: "text";
measures: Name[];
/** default value is "column" */
measuresPlacement?: "row" | "column";
measuresPlacement?: MeasuresPlacement;
}

/**
@@ -75,37 +91,24 @@ interface TextOnlyPivotGridProps<Name extends string> {
type GeneralPivotGridProps<Name extends string> =
| {
type: "general";
cell: (prop: {
data: FragmentFrame<Name>;
width: number;
height: number;
row: number;
column: number;
}) => React.ReactElement | null;
cell: (prop: CellPropsWithoutMeasure<Name>) => React.ReactElement | null;
}
| {
type: "generalWithMeasures";
measures: Name[];
/** default value is "column" */
measuresPlacement?: "row" | "column";
cell: (prop: {
data: FragmentFrame<Name>;
width: number;
height: number;
row: number;
column: number;
measure: Name;
}) => React.ReactElement | null;
measuresPlacement?: MeasuresPlacement;
cell: (prop: CellPropsWithMeasure<Name>) => React.ReactElement | null;
};

interface Accessors<Name extends string> {
width?: (p: WidthParam<Name>) => number;
height?: (p: HeightParam<Name>) => number;
width?: WidthAccessor<Name>;
height?: HeightAccessor<Name>;
}

interface Axes {
row?: (prop: { row: number; measure?: string; width: number; height: number }) => React.ReactElement | null;
column?: (prop: { column: number; measure?: string; width: number; height: number }) => React.ReactElement | null;
interface Axes<Name extends string> {
row?: (prop: RowProps<Name>) => React.ReactElement | null;
column?: (prop: ColumnProps<Name>) => React.ReactElement | null;
}

interface PivotGridStyle {
@@ -121,9 +124,9 @@ type Props<Name extends string = string> = (TextOnlyPivotGridProps<Name> | Gener
height: number;
data: PivotFrame<Name>;
style?: PivotGridStyle;
axes?: Axes;
axes?: Axes<Name>;
accessors?: Accessors<Name>;
header?: (prop: { value: string; width: number; height: number }) => React.ReactElement | null;
header?: (prop: { width: number; height: number; value: string }) => React.ReactElement | null;
dimensionLabels?: DimensionLabels | "top" | "left" | "none";
};

@@ -142,10 +145,10 @@ export const PivotGrid = React.memo(<Name extends string = string>(props: Props<
const { data } = props;
const cell = "cell" in props ? props.cell : defaultCell;
const header = props.header || defaultHeader;
const axes = props.axes || (emptyObject as Axes);
const axes = props.axes || (emptyObject as Axes<Name>);
const accessors = props.accessors || (emptyObject as Accessors<Name>);
const heightAccessors = accessors.height || (defaultHeight as Defined<Accessors<Name>["height"]>);
const widthAccessors = accessors.width || (defaultWidth as Defined<Accessors<Name>["width"]>);
const heightAccessors = accessors.height || (defaultHeight as HeightAccessor<Name>);
const widthAccessors = accessors.width || (defaultWidth as WidthAccessor<Name>);
const dimensionLabels = useMemo(
() => (dimensionLabelsShortcut(props.dimensionLabels) || { row: "none", column: "none" }) as DimensionLabels,
[props.dimensionLabels],
@@ -194,14 +197,14 @@ export const PivotGrid = React.memo(<Name extends string = string>(props: Props<

const rowHeight = useCallback(
(rowIndex: number) =>
heightAccessors(coordinateToHeightParam(indexToCoordinateMemoised({ rowIndex, columnIndex: 0 }))),
[heightAccessors, indexToCoordinateMemoised],
heightAccessors({ data, ...coordinateToHeightParam(indexToCoordinateMemoised({ rowIndex, columnIndex: 0 })) }),
[heightAccessors, indexToCoordinateMemoised, data],
);

const columnWidth = useCallback(
(columnIndex: number) =>
widthAccessors(coordinateToWidthParam(indexToCoordinateMemoised({ rowIndex: 0, columnIndex }))),
[widthAccessors, indexToCoordinateMemoised],
widthAccessors({ data, ...coordinateToWidthParam(indexToCoordinateMemoised({ rowIndex: 0, columnIndex })) }),
[widthAccessors, indexToCoordinateMemoised, data],
);

/**
@@ -240,12 +243,12 @@ export const PivotGrid = React.memo(<Name extends string = string>(props: Props<
};

item = React.createElement(cell, {
data: data.cell(cellCoordinates.row, cellCoordinates.column),
data,
width,
height,
measure: cellCoordinates.measure!,
row: cellCoordinates.row,
column: cellCoordinates.column,
height,
width,
});
break;
case "RowHeader":
@@ -281,20 +284,22 @@ export const PivotGrid = React.memo(<Name extends string = string>(props: Props<
case "RowAxis":
if (axes.row) {
item = React.createElement(axes.row, {
data,
width,
height,
row: cellCoordinates.row,
measure: cellCoordinates.measure,
height,
width,
});
}
break;
case "ColumnAxis":
if (axes.column) {
item = React.createElement(axes.column, {
data,
width,
height,
column: cellCoordinates.column,
measure: cellCoordinates.measure,
height,
width,
});
}
break;
@@ -1,11 +1,11 @@
import { PivotFrame } from "@operational/frame";
import { CellCoordinates, DimensionLabels, HeightParam, WidthParam } from "./types";
import { CellCoordinates, DimensionLabels, HeightParam, WidthParam, MeasuresPlacement } from "./types";

export const exhaustiveCheck = (_: never) => undefined;

export type IndexToCoordinate = <Name extends string = string>(prop: {
rowHeadersCount: number;
measuresPlacement: "row" | "column";
measuresPlacement: MeasuresPlacement;
columnHeadersCount: number;
measuresCount: number;
data: PivotFrame<Name>;
@@ -296,36 +296,39 @@ export const coordinateToWidthParam = <Name extends string = string>(prop: CellC
case "Empty":
if (prop.measure && prop.rowIndex === undefined) {
return {
measure: true,
type: "RowMeasure",
};
} else if (prop.axis && prop.rowIndex === undefined) {
return {
axis: true,
type: "RowAxis",
};
} else {
return {
type: "RowHeader",
rowIndex: prop.rowIndex!,
};
}
case "Cell":
case "ColumnAxis":
case "ColumnHeader":
return {
type: "Cell",
column: prop.column,
measure: prop.measure,
};
case "RowAxis":
return {
axis: true,
type: "RowAxis",
};
case "RowHeader":
if (prop.rowIndex !== undefined) {
return {
type: "RowHeader",
rowIndex: prop.rowIndex,
};
} else {
return {
measure: true,
type: "RowMeasure",
};
}
default:
@@ -341,36 +344,39 @@ export const coordinateToHeightParam = <Name extends string = string>(
case "Empty":
if (prop.measure && prop.columnIndex === undefined) {
return {
measure: true,
type: "ColumnMeasure",
};
} else if (prop.axis && prop.columnIndex === undefined) {
return {
axis: true,
type: "ColumnAxis",
};
} else {
return {
type: "ColumnHeader",
columnIndex: prop.columnIndex!,
};
}
case "Cell":
case "RowAxis":
case "RowHeader":
return {
type: "Cell",
row: prop.row,
measure: prop.measure,
};
case "ColumnAxis":
return {
axis: true,
type: "ColumnAxis",
};
case "ColumnHeader":
if (prop.columnIndex !== undefined) {
return {
type: "ColumnHeader",
columnIndex: prop.columnIndex,
};
} else {
return {
measure: true,
type: "ColumnMeasure",
};
}
default:
@@ -398,7 +404,7 @@ export const getRowHeadersCount = <Name extends string = string>({
};
data: PivotFrame<Name>;
dimensionLabels: DimensionLabels;
measuresPlacement: "row" | "column";
measuresPlacement: MeasuresPlacement;
measuresCount: number;
}) => {
const rowsDepth = data.getPivotRows().length;
@@ -440,7 +446,7 @@ export const getColumnHeadersCount = <Name extends string = string>({
};
data: PivotFrame<Name>;
dimensionLabels: DimensionLabels;
measuresPlacement: "row" | "column";
measuresPlacement: MeasuresPlacement;
measuresCount: number;
}) => {
const columnDepth = data.getPivotColumns().length;
@@ -469,7 +475,7 @@ export const getColumnCount = <Name extends string = string>({
measuresCount,
}: {
data: PivotFrame<Name>;
measuresPlacement: "row" | "column";
measuresPlacement: MeasuresPlacement;
measuresCount: number;
rowHeadersCount: number;
}) => {
@@ -491,7 +497,7 @@ export const getRowCount = <Name extends string = string>({
measuresCount,
}: {
data: PivotFrame<Name>;
measuresPlacement: "row" | "column";
measuresPlacement: MeasuresPlacement;
measuresCount: number;
columnHeadersCount: number;
}) => {
ProTip! Use n and p to navigate between commits in a pull request.
You can’t perform that action at this time.