Browse Source

Merge branch 'grid-class' into 'main'

Grid class

See merge request chartwerk/core!4
merge-requests/6/merge
Alexander Velikiy 3 years ago
parent
commit
17b7a167fa
  1. 93
      src/components/grid.ts
  2. 72
      src/index.ts
  3. 27
      src/types.ts

93
src/components/grid.ts

@ -0,0 +1,93 @@
import { GridOptions, SvgElParams } from '../types';
// we import only d3 types here
import * as d3 from 'd3';
import defaultsDeep from 'lodash/defaultsDeep';
const DEFAULT_GRID_TICK_COUNT = 5;
const DEFAULT_GRID_OPTIONS: GridOptions = {
x: {
enabled: true,
ticksCount: DEFAULT_GRID_TICK_COUNT,
},
y: {
enabled: true,
ticksCount: DEFAULT_GRID_TICK_COUNT,
},
}
// Grid Class - is a core component, which can be a separate Pod in the future. (but not in current Pod terminology)
// All components have construcor with required args: svg element which will be filled with this component and options for it.
// All compoтents have a reqiured method "render", which will be called in core costructor. <- this solution is temporary.
// Each component has its own default options.
// svgElement should be a separate class with its own height, width, xScale, yScale params to avoid SvgElParams as argument.
// We have a general problem with passing d3 as argument everywhere. Fix it, and remove from arg in constructor here.
export class Grid {
protected gridOptions: GridOptions;
constructor(
private _d3: typeof d3,
private _svgEl: d3.Selection<SVGElement, unknown, null, undefined>,
private _svgElParams: SvgElParams,
_gridOptions: GridOptions,
) {
this.gridOptions = this.setOptionDefaults(_gridOptions);
}
protected setOptionDefaults(gridOptions: GridOptions): GridOptions {
return defaultsDeep(gridOptions, DEFAULT_GRID_OPTIONS);
}
public render(): void {
// TODO: temporary. Move out of here
this._svgEl.selectAll('.grid').remove();
this.renderGridLinesX();
this.renderGridLinesY();
this.updateStylesOfTicks();
}
renderGridLinesX(): void {
if(!this.gridOptions.x.enabled) {
return;
}
this._svgEl
.append('g')
.attr('transform', `translate(0,${this._svgElParams.height})`)
.attr('class', 'grid x-grid')
.style('pointer-events', 'none')
.call(
this._d3.axisBottom(this._svgElParams.xScale)
.ticks(this.gridOptions.x.ticksCount)
.tickSize(-this._svgElParams.height)
.tickFormat(() => '')
);
}
renderGridLinesY(): void {
if(!this.gridOptions.y.enabled) {
return;
}
this._svgEl
.append('g')
.attr('class', 'grid y-grid')
.style('pointer-events', 'none')
.call(
this._d3.axisLeft(this._svgElParams.yScale)
.ticks(this.gridOptions.y.ticksCount)
.tickSize(-this._svgElParams)
.tickFormat(() => '')
);
}
updateStylesOfTicks(): void {
// TODO: add options for these actions
this._svgEl.selectAll('.grid').selectAll('.tick')
.attr('opacity', '0.5');
this._svgEl.selectAll('.grid').select('.domain')
.style('pointer-events', 'none');
}
}

72
src/index.ts

@ -1,5 +1,6 @@
import VueChartwerkPodMixin from './VueChartwerkPodMixin';
import { PodState } from './state';
import { Grid } from './components/grid';
import styles from './css/style.css';
@ -17,7 +18,8 @@ import {
PanOrientation,
yAxisOrientation,
ScrollPanOrientation,
AxisOption
AxisOption,
SvgElParams,
} from './types';
import { uid } from './utils';
import { palette } from './colors';
@ -101,16 +103,6 @@ const DEFAULT_OPTIONS: Options = {
format: AxisFormat.NUMERIC
}
},
grid: {
x: {
isActive: true,
ticksCount: 5,
},
y: {
isActive: true,
ticksCount: 5,
},
},
crosshair: {
orientation: CrosshairOrientation.VERTICAL,
color: 'red'
@ -146,6 +138,9 @@ abstract class ChartwerkPod<T extends TimeSerie, O extends Options> {
protected deltaYTransform = 0;
protected debouncedRender = debounce(this.render.bind(this), 100);
// components
protected grid: Grid;
// TODO: test variables instead of functions with cache
private _xScale: d3.ScaleLinear<number, number> | null = null;
private _yScale: d3.ScaleLinear<number, number> | null = null;
@ -172,6 +167,9 @@ abstract class ChartwerkPod<T extends TimeSerie, O extends Options> {
this.d3Node = this.d3.select(this.el);
this.addEventListeners();
this.createSvg();
this.initComponents();
}
protected addEventListeners(): void {
@ -185,7 +183,6 @@ abstract class ChartwerkPod<T extends TimeSerie, O extends Options> {
public render(): void {
this.clearScaleCache();
this.renderSvg();
this.renderAxes();
this.renderGrid();
@ -237,11 +234,23 @@ abstract class ChartwerkPod<T extends TimeSerie, O extends Options> {
public abstract renderSharedCrosshair(values: { x?: number, y?: number }): void;
public abstract hideSharedCrosshair(): void;
protected initPodState() {
protected initPodState(): void {
this.state = new PodState(this.options);
}
protected renderSvg(): void {
protected initComponents(): void {
// TODO: make chartContainer a separate class with SvgElParams inside to avoid duplication
const svgElParams = {
height: this.height,
width: this.width,
xScale: this.xScale,
yScale: this.yScale,
}
this.grid = new Grid(this.d3, this.chartContainer, svgElParams, this.options.grid);
}
protected createSvg(): void {
this.d3Node.select('svg').remove();
this.svg = this.d3Node
.append('svg')
@ -254,40 +263,7 @@ abstract class ChartwerkPod<T extends TimeSerie, O extends Options> {
}
protected renderGrid(): void {
this.chartContainer.selectAll('.grid').remove();
if(this.options.grid.x.isActive) {
this.chartContainer
.append('g')
.attr('transform', `translate(0,${this.height})`)
.attr('class', 'grid x-grid')
.style('pointer-events', 'none')
.call(
this.d3.axisBottom(this.xScale)
.ticks(this.options.grid.x.ticksCount)
.tickSize(-this.height)
.tickFormat(() => '')
);
}
if(this.options.grid.y.isActive) {
this.chartContainer
.append('g')
.attr('class', 'grid y-grid')
.style('pointer-events', 'none')
.call(
this.d3.axisLeft(this.yScale)
.ticks(this.options.grid.y.ticksCount)
.tickSize(-this.width)
.tickFormat(() => '')
);
}
this.chartContainer.selectAll('.grid').selectAll('.tick')
.attr('opacity', '0.5');
this.chartContainer.selectAll('.grid').select('.domain')
.style('pointer-events', 'none');
this.grid.render();
}
protected renderAxes(): void {

27
src/types.ts

@ -32,16 +32,7 @@ export type Options = {
y?: AxisOption,
y1?: AxisOption
};
grid?: {
x?: {
isActive?: boolean;
ticksCount?: number;
},
y?: {
isActive?: boolean;
ticksCount?: number;
},
};
grid?: GridOptions;
crosshair?: {
orientation?: CrosshairOrientation;
color?: string;
@ -95,6 +86,16 @@ export type Options = {
renderTicksfromTimestamps?: boolean;
renderLegend?: boolean;
};
export type GridOptions = {
x?: {
enabled?: boolean;
ticksCount?: number;
},
y?: {
enabled?: boolean;
ticksCount?: number;
},
}
export type AxisOption = {
isActive?: boolean;
ticksCount?: number;
@ -166,3 +167,9 @@ export enum yAxisOrientation {
RIGHT = 'right',
BOTH = 'both'
}
export type SvgElParams = {
height: number,
width: number,
xScale: d3.ScaleLinear<number, number>,
yScale: d3.ScaleLinear<number, number>,
}

Loading…
Cancel
Save