Skip to content

Commit

Permalink
feat: add d3fc-chart types (d3fc#1697)
Browse files Browse the repository at this point in the history
  • Loading branch information
moxon6 authored Apr 28, 2021
1 parent b294f0a commit fe9289b
Show file tree
Hide file tree
Showing 13 changed files with 4,386 additions and 995 deletions.
18 changes: 18 additions & 0 deletions .eslintrc.json
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,24 @@
"jest": true,
"node": true
}
},
{
"files": [
"./packages/**/*.ts"
],
"parser": "@typescript-eslint/parser",
"plugins": [
"@typescript-eslint"
],
"extends": [
"plugin:@typescript-eslint/eslint-recommended",
"plugin:@typescript-eslint/recommended"
],
"rules": {
"@typescript-eslint/no-explicit-any": "off",
"@typescript-eslint/semi": "error",
"@typescript-eslint/member-delimiter-style": "error"
}
}
]
}
7 changes: 5 additions & 2 deletions examples/simple-chart/index.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
/// <reference path="../../packages/d3fc/index.d.ts" />

const data = fc.randomFinancial()(50);

const yExtent = fc.extentLinear().accessors([d => d.high, d => d.low]);
Expand All @@ -10,10 +12,11 @@ const multi = fc.seriesSvgMulti().series([gridlines, candlestick]);

const chart = fc
.chartCartesian(d3.scaleTime(), d3.scaleLinear())
.yDomain(yExtent(data))
.xDomain(xExtent(data))
.svgPlotArea(multi);

chart.xDomain(xExtent(data));
chart.yDomain(yExtent(data));

d3.select('#chart')
.datum(data)
.call(chart);
4,951 changes: 3,965 additions & 986 deletions package-lock.json

Large diffs are not rendered by default.

15 changes: 11 additions & 4 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,14 @@
"publish": "lerna publish --conventional-commits --message \"chore: publish\" --yes",
"bundle": "lerna run bundle --stream --concurrency 1",
"bundle-min": "lerna run bundle --stream --concurrency 1 -- -- --configEnv=prod",
"test": "jest --config scripts/jest/jest.config.js",
"eslint": "eslint \"**/*.js\"",
"test": "jest --config scripts/jest/jest.config.js && npm run test:types",
"eslint": "eslint . --ext .js --ext .ts",
"markdownlint": "markdownlint **/*.md --ignore **/node_modules/**",
"lint": "npm run eslint && npm run markdownlint",
"start": "lerna run bundle && npm start --prefix ./packages/d3fc",
"commitlint": "commitlint --from HEAD~1 to HEAD --verbose",
"examples": "cd examples && npx jest --runInBand"
"examples": "cd examples && npx jest --runInBand",
"test:types": "lerna run test:types"
},
"prettier": {
"singleQuote": true,
Expand Down Expand Up @@ -42,6 +43,10 @@
"@commitlint/cli": "^8.1.0",
"@commitlint/config-conventional": "^8.1.0",
"@commitlint/travis-cli": "^8.1.0",
"@types/d3": "^6.3.0",
"@types/jest": "^26.0.22",
"@typescript-eslint/eslint-plugin": "^4.22.0",
"@typescript-eslint/parser": "^4.22.0",
"babelrc-rollup": "^3.0.0",
"canvas": "^2.6.1",
"chalk": "^2.4.2",
Expand Down Expand Up @@ -75,7 +80,9 @@
"rollup-plugin-livereload": "^2.0.0",
"rollup-plugin-node-resolve": "^5.2.0",
"rollup-plugin-serve": "^1.0.4",
"seedrandom": "^3.0.5"
"seedrandom": "^3.0.5",
"tsd": "^0.14.0",
"typescript": "^4.2.3"
},
"config": {
"commitizen": {
Expand Down
3 changes: 3 additions & 0 deletions packages/d3fc-chart/index.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export { default as chartSvgCartesian } from './src/svg/cartesian';
export { default as chartCanvasCartesian } from './src/canvas/cartesian';
export { default as chartCartesian } from './src/cartesian';
3 changes: 2 additions & 1 deletion packages/d3fc-chart/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,8 @@
},
"scripts": {
"bundle": "npx rollup -c ../../scripts/rollup.config.js",
"start": "npm start --prefix ../d3fc -- --configPkg=d3fc-chart"
"start": "npm start --prefix ../d3fc -- --configPkg=d3fc-chart",
"test:types": "tsd"
},
"dependencies": {
"@d3fc/d3fc-axis": "^3.0.5",
Expand Down
294 changes: 294 additions & 0 deletions packages/d3fc-chart/src/cartesian.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,294 @@
import type { ScaleIdentity } from 'd3-scale';
import type { Axis } from 'd3-axis';

// To be eventually replaced by type from @d3fc/d3fc-axis
interface AxisD3fc<Domain> extends Axis<Domain> {
tickCenterLabel(): boolean;
tickCenterLabel(tickCenterLabel: boolean): this;
}

export type Functor<T> = ((...args: any[]) => T);

type TypeOrFunctor<T> = T | Functor<T>;

type AnyFunction = (...args: any[]) => any;

export interface WebglPlotAreaComponent {
(d: any): any;
context(canvas: HTMLCanvasElement): this;
pixelRatio(pixelRatio: number): this;
xScale(scale: any): this;
yScale(scale: any): this;
}

export interface CanvasPlotAreaComponent {
(d: any): any;
context(canvas: HTMLCanvasElement): this;
xScale(scale: any): this;
yScale(scale: any): this;
}

export interface SvgPlotAreaComponent {
(d: any): any;
xScale(scale: any): this;
yScale(scale: any): this;
}

type Decorator = (container: d3.Selection<any, any, any, any>, data: any, index: number) => void;

type PrefixProperties<T, Prefix extends string> = {
[Property in keyof T as `${Prefix}${Capitalize<string & Property>}`]: T[Property]
};

type AnyMethods<T> = {
[Property in keyof T]: T[Property] extends AnyFunction ? AnyFunction : T[Property]
};

// eslint-disable-next-line @typescript-eslint/no-unused-vars
type NotStartsWith<K, TPrefix extends string> = K extends `${TPrefix}${infer _}` ? never : K;

type OmitPrefixes<T> = {[K in keyof T as NotStartsWith<K, 'range' | 'tickFormat'> ]: T[K]};

type XOrient = 'top' | 'bottom' | 'none';
type YOrient = 'left' | 'right' | 'none';

/**
* Cartesian Chart
*/
export type CartesianChart<XScale, YScale> = {
(selection: d3.Selection<any, any, any, any>): void;

/**
* Returns the existing component.
*/
canvasPlotArea(): CanvasPlotAreaComponent | null;

/**
* Sets the component to render onto the canvas, and returns the Cartesian chart.
* For series that contain a very high number of data-points, rendering to canvas can reduce the rendering time and improve performance.
* For `canvasPlotArea` and `webglPlotArea`, the relevant context is automatically applied to the chart.
* @param component
*/
canvasPlotArea(component: CanvasPlotAreaComponent): CartesianChart<XScale, YScale>;

/**
* Returns a function that returns chartLabel.
*/
chartLabel(): Functor<string>;

/**
* If `label` is specified, sets the text for the given label, and returns the Cartesian chart.
* The `label` value can either be a string, or a function that returns a string.
* If it is a function, it will be invoked with the data that is 'bound' to the chart.
* This can be useful if you are rendering multiple charts using a data join.
* @param label
*/
chartLabel(label: TypeOrFunctor<string>): CartesianChart<XScale, YScale>;

/**
* Returns the current decorator function.
*/
decorate(): Decorator;

/**
* Sets the decorator function to the specified, and returns the Cartesian chart.
* @param decorateFunc
*/
decorate(decorateFunc: Decorator): CartesianChart<XScale, YScale>;

/**
* Returns the existing component.
*/
svgPlotArea(): SvgPlotAreaComponent | null;

/**
* Sets the component to render onto the SVG, and returns the Cartesian chart.
* For components that require user-interaction, rendering to SVG can simplify their implementation.
* @param component
*/
svgPlotArea(component: SvgPlotAreaComponent): CartesianChart<XScale, YScale>;

/**
* Returns the current useDevicePixelRatio value.
*/
useDevicePixelRatio(): boolean;

/**
* Sets whether the Canvas / WebGL should be scaled based on the resolution of the display device, and returns the Cartesian chart.
* @param useDevicePixelRatio
*/
useDevicePixelRatio(useDevicePixelRatio: boolean): CartesianChart<XScale, YScale>;

/**
* Returns the existing component.
*/
webglPlotArea(): WebglPlotAreaComponent | null;

/**
* Sets the component to render, and returns the Cartesian chart.
* For `canvasPlotArea` and `webglPlotArea`, the relevant context is automatically applied to the chart.
* @param component
*/
webglPlotArea(component: WebglPlotAreaComponent): CartesianChart<XScale, YScale>;

/**
* Returns the x-axis height or null if not set.
*/
xAxisHeight(): Functor<string>;

/**
* If `height` is specified, sets the height for the x-axis, and returns the Cartesian chart.
* The value should be a string with units (e.g. "2em").
*
* The `height` value can either be a string, or a function that returns a string.
* If it is a function, it will be invoked with the data that is 'bound' to the chart.
*
* This can be useful if you are rendering multiple charts using a data join.
* @param height
*/
xAxisHeight(height: TypeOrFunctor<string>): CartesianChart<XScale, YScale>;

/**
* Returns the current decorator function.
*/
xDecorate(): Decorator;

/**
* Sets the decorator function to the specified, and returns the Cartesian chart.
* @param decorateFunc
*/
xDecorate(decorateFunc: Decorator): CartesianChart<XScale, YScale>;

/**
* Returns a function that returns xLabel.
*/
xLabel(): Functor<string>;

/**
* If `label` is specified, sets the text for the given label, and returns the Cartesian chart.
* The `label` value can either be a string, or a function that returns a string.
* If it is a function, it will be invoked with the data that is 'bound' to the chart.
* This can be useful if you are rendering multiple charts using a data join.
* @param label
*/
xLabel(label: TypeOrFunctor<string>): CartesianChart<XScale, YScale>;

/**
* Returns a function that returns the orientation.
*/
xOrient(): Functor<XOrient>;

/**
* Sets the orientation for the axis in the given direction, and returns the Cartesian chart.
* Valid values for `xOrient` are `"top"`, `"bottom"` or `"none"`.
* The value can either be a string, or a function that returns a string.
* If it is a function, it will be invoked with the data that is 'bound' to the chart.
* This can be useful if you are rendering multiple charts using a data join.
* @param orient
*/
xOrient(orient: TypeOrFunctor<XOrient>): CartesianChart<XScale, YScale>;

/**
* Returns the y-axis width or null if not set.
*/
yAxisWidth(): Functor<string>;

/**
* If `width` is specified, sets the width for the y-axis, and returns the Cartesian chart.
* The value should be a string with units (e.g. "2em").
*
* The `width` value can either be a string, or a function that returns a string.
* If it is a function, it will be invoked with the data that is 'bound' to the chart.
*
* This can be useful if you are rendering multiple charts using a data join.
* @param width
*/
yAxisWidth(width: TypeOrFunctor<string>): CartesianChart<XScale, YScale>;

/**
* Returns the current decorator function.
*/
yDecorate(): Decorator;

/**
* Sets the decorator function to the specified, and returns the Cartesian chart.
* @param decorateFunc
*/
yDecorate(decorateFunc: Decorator): CartesianChart<XScale, YScale>;

/**
* Returns a function that returns yLabel.
*/
yLabel(): Functor<string>;

/**
* If `label` is specified, sets the text for the given label, and returns the Cartesian chart.
* The `label` value can either be a string, or a function that returns a string.
* If it is a function, it will be invoked with the data that is 'bound' to the chart.
* This can be useful if you are rendering multiple charts using a data join.
* @param label
*/
yLabel(label: TypeOrFunctor<string>): CartesianChart<XScale, YScale>;

/**
* Returns a function that returns the orientation.
*/
yOrient(): Functor<YOrient>;

/**
* Sets the orientation for the axis in the given direction, and returns the Cartesian chart.
* Valid values for `yOrient` are `"left"`, `"right"` or `"none"`.
* The value can either be a string, or a function that returns a string.
* If it is a function, it will be invoked with the data that is 'bound' to the chart.
* This can be useful if you are rendering multiple charts using a data join.
* @param orient
*/
yOrient(orient: TypeOrFunctor<YOrient>): CartesianChart<XScale, YScale>;
}
& AnyMethods<PrefixProperties<OmitPrefixes<XScale>, 'x'>>
& AnyMethods<PrefixProperties<OmitPrefixes<XScale>, 'y'>>
& AnyMethods<PrefixProperties<AxisD3fc<any>, 'x'>>
& AnyMethods<PrefixProperties<AxisD3fc<any>, 'y'>>;

export type Fallback<T> = undefined extends T ? ScaleIdentity : T;

export interface Scale {
range: any;
domain: any;
}

export interface CartesianChartConfigurationParameter<XScale, YScale> {
xScale?: XScale;
yScale?: YScale;
xAxis?: {
top?: any;
bottom?: any;
};
yAxis?: {
left?: any;
right?: any;
};
}

// -------------------------------------------------------------------------------
// Cartesian Chart Factory
// -------------------------------------------------------------------------------

/**
* Constructs a new Cartesian chart with the given scales and axis components.
* If xAxis is specified, it must be an object with the required x-axis factory function (top if xOrient="top" or bottom if xOrient="bottom").
* If yAxis is specified, it must be an object with the required y-axis factory function (left if yOrient="left" or right if yOrient="right").
* @param configuration
*/
export default function Cartesian<XScale extends Scale | undefined, YScale extends Scale | undefined>(configuration: CartesianChartConfigurationParameter<XScale, YScale>)
: CartesianChart<Fallback<XScale>, Fallback<YScale>>;

/**
* Constructs a new Cartesian chart with the given scales.
* @param xScale
* @param yScale
*/
export default function Cartesian<XScale extends Scale | undefined, YScale extends Scale | undefined>(xScale?: XScale, yScale?: YScale)
: CartesianChart<Fallback<XScale>, Fallback<YScale>>;

export { };
1 change: 1 addition & 0 deletions packages/d3fc-chart/src/css.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export const css: string;
Loading

0 comments on commit fe9289b

Please sign in to comment.