Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions packages/d3chart/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
# Change Log

All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.


12 changes: 12 additions & 0 deletions packages/d3chart/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
# ![](docs/static/favicon.png) Candela D3Chart

Candela D3Chart provides a convenient mixin for constructing freeform
visualization components conforming to the D3 reusable chart paradigm.

Candela is an open-source framework for creating interoperable, reusable
visualization components for the web. Candela is a part of
[Kitware](http://www.kitware.com)'s [Resonant](http://resonant.kitware.com)
platform. Candela focuses on making scalable, rich visualizations available with
a normalized API for use in real-world data science applications.

Please see our documentation at https://candela.readthedocs.io.
45 changes: 45 additions & 0 deletions packages/d3chart/karma.conf.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
var path = require('path');

var kconfig = {
singleRun: true,
client: {
captureConsole: false
},
browsers: [
'ChromeHeadless'
],
frameworks: [
'tap'
],
reporters: [
'tap-pretty'
],
tapReporter: {
prettify: require('tap-spec')
},
files: [
'build/test.unit.js'
]
};

if (process.env.COVERAGE) {
kconfig.files = [
'build/test.coverage.js'
];
kconfig.reporters = [
'coverage-istanbul'
];
kconfig.coverageIstanbulReporter = {
reports: ['text-summary', 'html'],
dir: path.resolve('../../build/coverage'),
'report-config': {
html: {
subdir: 'html/core'
}
}
};
}

module.exports = function (config) {
config.set(kconfig);
};
58 changes: 58 additions & 0 deletions packages/d3chart/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
{
"name": "@candela/d3chart",
"version": "0.23.3",
"description": "Candela D3Chart mixin library",
"main": "dist/candela-d3chart.js",
"scripts": {
"build": "webpack --mode development",
"build:prod": "webpack --mode production",
"clean": "rm -rf dist build",
"lint": "semistandard | snazzy",
"build:test": "webpack --config webpack.config.test.babel.js --mode development",
"test": "karma start --colors --log-level error"
},
"publishConfig": {
"access": "public"
},
"babel": {
"presets": [
"@babel/env"
]
},
"keywords": [],
"author": "Kitware Inc.",
"license": "Apache-2.0",
"semistandard": {
"ignore": [
"dist",
"build"
]
},
"dependencies": {
"@candela/core": "^0.23.3",
"@candela/events": "^0.23.3",
"@candela/size": "^0.23.3",
"d3-selection": "^1.1.0",
"d3-transition": "^1.1.0"
},
"devDependencies": {
"@babel/core": "^7.0.0-beta.44",
"@babel/preset-env": "^7.0.0-beta.44",
"@babel/register": "^7.0.0-beta.44",
"babel-loader": "^8.0.0-beta.2",
"d3-selection": "^1.1.0",
"istanbul-instrumenter-loader": "^3.0.1",
"karma": "^2.0.2",
"karma-chrome-launcher": "^2.2.0",
"karma-coverage-istanbul-reporter": "^1.4.2",
"karma-tap": "^4.1.3",
"karma-tap-pretty-reporter": "^4.0.0",
"semistandard": "^7.0.5",
"snazzy": "^4.0.0",
"tap-spec": "^4.1.1",
"tape": "^4.9.0",
"tape-catch": "^1.0.6",
"webpack": "^4.6.0",
"webpack-cli": "^2.0.14"
}
}
116 changes: 116 additions & 0 deletions packages/d3chart/src/Axes.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
import { axisLeft, axisBottom } from 'd3-axis';

import { D3Chart } from './D3Chart';

const scaleFuncMap = {
left: axisLeft,
bottom: axisBottom
};

function getLabelOffset (dir, bounds) {
switch (dir) {
case 'left':
return {
rotate: 'rotate(-90)',
x: -bounds.height / 2,
y: -40
};

case 'bottom':
return {
x: bounds.width / 2,
y: 40
};

default:
throw new Error(`illegal direction parameter: "${dir}"`);
}
}

class AxesImpl {
constructor (that) {
this.that = that;

this.scale = {
left: null,
bottom: null
};

this.axis = {
left: null,
bottom: null
};

this.group = {
left: null,
bottom: null
};
}

setScale (direction, scale) {
const scaleFunc = scaleFuncMap[direction];

const bounds = this.that.margin.bounds('plot');

if (direction === 'left' || direction === 'right') {
scale.range([bounds.height, 0]);
} else {
scale.range([0, bounds.width]);
}

this.scale[direction] = scale;

let axis = this.group[direction];
if (!axis) {
axis = this.group[direction] = this.that.d3chart[direction].append('g');
axis.append('text')
.attr('fill', '#000')
.style('font-size', '11pt')
.style('font-weight', 'bold');

if (direction === 'left') {
const margin = this.that.margin.get();
axis.attr('transform', `translate(${margin.left},0)`);
}
} else {
axis.selectAll('*').remove();
}

axis.call(this.axis[direction] = scaleFunc(scale));

return this;
}

getAxis (dir) {
return this.axis[dir];
}

getScale (dir) {
return this.scale[dir];
}

setLabel (dir, label) {
const bounds = this.that.margin.bounds(dir);
const text = this.group[dir].select('text');
const offset = getLabelOffset(dir, bounds);

text.attr('transform', offset.rotate ? offset.rotate : null)
.attr('x', offset.x)
.attr('y', offset.y)
.text(label);

return this;
}

renderAxis (dir) {
this.group[dir].call(this.axis[dir]);
return this;
}
}

export const Axes = Base => class extends D3Chart(Base) {
constructor () {
super(...arguments);
this.axes = new AxesImpl(this);
}
};
93 changes: 93 additions & 0 deletions packages/d3chart/src/Crosshairs.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
import { D3Chart } from './D3Chart';

class CrosshairsImpl {
constructor (that) {
this.that = that;

this.target = null;
this.crosshairX = null;
this.crosshairY = null;
}

init () {
const that = this.that;

const plotBounds = that.margin.bounds('plot');
this.target = that.d3chart.plot.append('rect', ':first-child')
.classed('crosshairs-target', true)
.attr('width', plotBounds.width)
.attr('height', plotBounds.height)
.style('opacity', 0.0);

const g = that.d3chart.plot.append('g')
.classed('crosshairs', true)
.style('pointer-events', 'none');

this.crosshairX = g.append('line')
.classed('crosshair-x', true)
.style('opacity', 0)
.style('stroke', 'lightgray')
.attr('x1', 0)
.attr('x2', plotBounds.width);

this.crosshairY = g.append('line')
.classed('crosshair-y', true)
.style('opacity', 0)
.style('stroke', 'lightgray')
.attr('y1', 0)
.attr('y2', plotBounds.height);

this.target.on('mouseover.crosshairs', () => {
this.show();
}).on('mousemove.crosshairs', () => {
const mouse = this.mouseCoords();
this.setPosition(mouse.x, mouse.y);
}).on('mouseout.crosshairs', () => {
this.hide();
});
}

mouseCoords () {
const event = window.event;
if (event) {
const bbox = this.target.node().getBoundingClientRect();
return {
x: event.pageX - bbox.left,
y: event.pageY - bbox.top
};
}
}

setPosition (x, y) {
this.crosshairX.attr('y1', y)
.attr('y2', y);

this.crosshairY.attr('x1', x)
.attr('x2', x);

return this;
}

show () {
this.crosshairX.style('opacity', 1);
this.crosshairY.style('opacity', 1);

return this;
}

hide () {
this.crosshairX.style('opacity', 0);
this.crosshairY.style('opacity', 0);

return this;
}
}

export const Crosshairs = Base => class extends D3Chart(Base) {
constructor () {
super(...arguments);
if (!this.crosshairs) {
this.crosshairs = new CrosshairsImpl(this);
}
}
};
Loading