Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

@redsift/d3-rs-lines

Package Overview
Dependencies
Maintainers
3
Versions
35
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@redsift/d3-rs-lines - npm Package Compare versions

Comparing version 0.0.2 to 0.0.3

4

index.js

@@ -7,3 +7,5 @@ export {

export {
default as html
default as html,
intervals as timeIntervals,
timeMultiFormat as timeMultiFormat
} from "./src/lines";
{
"name": "@redsift/d3-rs-lines",
"version": "0.0.2",
"version": "0.0.3",
"description": "Generates line charts using D3v4.",

@@ -25,4 +25,4 @@ "keywords": [

"scripts": {
"serve": "gulp -o d3-rs-lines -g d3-selection -g d3-shape -g d3-array -g d3-scale -g d3-axis serve",
"build": "gulp -o d3-rs-lines -g d3-selection -g d3-shape -g d3-array -g d3-scale -g d3-axis build",
"serve": "gulp -o d3-rs-lines -g d3-selection serve",
"build": "gulp -o d3-rs-lines -g d3-selection build",
"pretest": "npm outdated && npm run build",

@@ -36,3 +36,3 @@ "test": "tape 'test/**/*-test.js' && eslint index.js src",

"d3-array": "^1.0.0",
"d3-scale": "^1.0.0",
"d3-scale": "^1.0.1",
"d3-axis": "^1.0.0",

@@ -42,3 +42,6 @@ "d3-time": "^1.0.0",

"d3-time-format": "^2.0.0",
"@redsift/d3-rs-svg": "~0.2.3",
"d3-voronoi": "^1.0.1",
"d3-polygon": "^1.0.0",
"@redsift/d3-rs-svg": "~0.2.5",
"@redsift/d3-rs-legends": "~0.0.3",
"@redsift/d3-rs-theme": "~0.0.2",

@@ -52,3 +55,3 @@ "@redsift/d3-rs-tip": "~0.2.3",

"del": "^2.2.1",
"eslint": "^2.13.1",
"eslint": "^3.0.1",
"gulp": "^3.9.1",

@@ -59,7 +62,6 @@ "gulp-rename": "^1.2.2",

"gulp-util": "^3.0.7",
"json2module": "~0.0.3",
"rollup": "^0.33.0",
"rollup": "^0.34.1",
"rollup-plugin-node-resolve": "^1.5.0",
"rollup-plugin-commonjs": "^3.0.0",
"rollup-plugin-buble": "^0.11.0",
"rollup-plugin-buble": "^0.12.1",
"rollup-plugin-json": "^2.0.0",

@@ -69,4 +71,4 @@ "rollup-stream": "^1.8.0",

"vinyl-source-stream": "^1.1.0",
"yargs": "^4.7.1"
"yargs": "^4.8.0"
}
}

@@ -59,4 +59,23 @@ # d3-rs-lines

`minValue`,`maxValue`|*Number* Sets the minimum and maximum Value scale range. Note that for log scales, minValue must be > 0.|Y
`tickCountIndex`|Supports strings, https://github.com/d3/d3-time#intervals
`tickCountIndex`,`tickCountValue`|*Number, String, Interval Function* Hints at the number of ticks to set in the corresponding axis. Supports strings for example (time intervals)[https://github.com/d3/d3-time#intervals]
`tickMinorIndex`, `tickMinorValue`|*Number, String, Interval Function* Hints at the number of minor ticks to set in the corresponding axis.
`tickFormatIndex`, `tickFormatValue`|*String, Function* Sets the formatting string or function for the ticks
`tickDisplayIndex`, `tickDisplayValue`|*Function* Alternatively you can customise all tick presentation logic with this function
`curve`|*String, Function*, https://github.com/d3/d3-shape#curves, excluding closed and open curves. If a function is supplied, it should implement https://github.com/d3/d3-shape#custom-curves
`symbol`|*(Array of) String, Function* https://github.com/d3/d3-shape#symbolCircle or https://github.com/d3/d3-shape#custom-symbol-types
`legendOrientation`|*String* top, left, bottom, right, voronoi
`voronoiAttraction`|*Number -1...0...1* When using the voronoi legendOrientation, how far the label is dragged to the data line. 0 implies no dragging, -1 pushes the labels away
`fill`|*String, Array, Function* If function, in addition to usual *data*, *index* parameters, a 3rd string parameter indicates the context - one of `area`, `stroke`, `symbol`, `legend`
`fillAreaOpacity`|*Unit Number*
`fillArea`, `fillStroke`|*Boolean, Array[Boolean]* Sets if the lines should be filled or stroked. By default is set appropriately for line and stack presentation
`stackOffset`, `stackOrder`|*String, Array, Function* https://github.com/d3/d3-shape#stack-orders https://github.com/d3/d3-shape#stack-offsets
`animation`|*String* `reveal`, `value`, `default`
`trim`|*Integer* Level to trim the array to
### Time
TODO: Explain string use and intervals like `utcDecade`;
TODO: `timeMultiFormat`
TODO: Discuss `timeMultiFormat` and `tickCountIndex` with relation to UTC
import { select } from 'd3-selection';
import { line, area, symbol } from 'd3-shape';
import { max, min } from 'd3-array';
import { line, area, symbol, stack } from 'd3-shape';
import { max, min, descending } from 'd3-array';
import { scaleLinear, scaleLog, scaleTime } from 'd3-scale';
import { axisBottom, axisLeft } from 'd3-axis';
import { timeFormat, timeFormatDefaultLocale } from 'd3-time-format';
import { format, formatDefaultLocale } from 'd3-format';
import { axisBottom, axisLeft, axisRight } from 'd3-axis';
import { timeFormat, timeFormatLocale, timeFormatDefaultLocale } from 'd3-time-format';
import { formatLocale, formatDefaultLocale } from 'd3-format';
import { voronoi } from 'd3-voronoi';
import { polygonArea, polygonCentroid } from 'd3-polygon';
import { nest } from 'd3-collection';
import {
stackOffsetExpand,
stackOffsetNone,
stackOffsetSilhouette,
stackOffsetWiggle,
stackOrderAscending,
stackOrderDescending,
stackOrderInsideOut,
stackOrderNone,
stackOrderReverse
} from 'd3-shape';
import {

@@ -65,4 +80,5 @@ timeMillisecond,

import { html as svg } from '@redsift/d3-rs-svg';
import { units, time } from "@redsift/d3-rs-intl";
import { tip } from "@redsift/d3-rs-tip";
import { svg as legends } from '@redsift/d3-rs-legends';
import { units, time } from '@redsift/d3-rs-intl';
import { tip } from '@redsift/d3-rs-tip';
import {

@@ -73,48 +89,48 @@ presentation10 as presentation10,

const intervals = {
export const intervals = {
timeMillisecond: [timeMillisecond, 1],
timeTnMillisecond: [timeMillisecond, 10],
timeHnMillisecond: [timeMillisecond, 100],
timeSecond: [timeSecond, 1],
timeTnSecond: [timeSecond, 10],
timeMinute: [timeMinute, 1],
timeHour: [timeHour, 1],
timeDay: [timeDay, 1],
timeBiDay: [timeDay, 2],
timeSunday: [timeSunday, 1],
timeMonday: [timeMonday, 1],
timeTuesday: [timeTuesday, 1],
timeWednesday: [timeWednesday, 1],
timeThursday: [timeThursday, 1],
timeFriday: [timeFriday, 1],
timeSaturday: [timeSaturday, 1],
timeWeek: [timeWeek, 1],
timeBiWeek: [timeWeek, 2],
timeMonth: [timeMonth, 1],
timeBiMonth: [timeMonth, 2],
timeQtYear: [timeMonth, 3],
timeYear: [timeYear, 1],
timeBiYear: [timeYear, 2],
timeDecade: [timeYear, 10],
utcMillisecond: [utcMillisecond, 1],
utcTnMillisecond: [utcMillisecond, 10],
utcHnMillisecond: [utcMillisecond, 100],
timeSecond: [timeSecond, 1],
timeTnSecond: [timeSecond, 10],
utcSecond: [utcSecond, 1],
utcTnSecond: [utcSecond, 10],
timeMinute: [timeMinute, 1],
utcMinute: [utcMinute, 1],
timeHour: [timeHour, 1],
utcHour: [utcHour, 1],
timeDay: [timeDay, 1],
timeBiDay: [timeDay, 2],
utcDay: [utcDay, 1],
utcBiDay: [utcDay, 2],
timeSunday: [timeSunday, 1],
utcSunday: [utcSunday, 1],
timeMonday: [timeMonday, 1],
utcMonday: [utcMonday, 1],
timeTuesday: [timeTuesday, 1],
utcTuesday: [utcTuesday, 1],
timeWednesday: [timeWednesday, 1],
utcWednesday: [utcWednesday, 1],
timeThursday: [timeThursday, 1],
utcThursday: [utcThursday, 1],
timeFriday: [timeFriday, 1],
utcFriday: [utcFriday, 1],
timeSaturday: [timeSaturday, 1],
utcSaturday: [utcSaturday, 1],
timeWeek: [timeWeek, 1],
timeBiWeek: [timeWeek, 2],
utcWeek: [utcWeek, 1],
utcBiWeek: [utcWeek, 2],
timeMonth: [timeMonth, 1],
timeBiMonth: [timeMonth, 2],
timeQtMonth: [timeMonth, 3],
utcMonth: [utcMonth, 1],
utcBiMonth: [utcMonth, 2],
utcTrMonth: [utcMonth, 3],
timeYear: [timeYear, 1],
timeBiYear: [timeYear, 2],
timeDecade: [timeYear, 10],
utcQtYear: [utcMonth, 3],
utcYear: [utcYear, 1],

@@ -148,30 +164,101 @@ utcBiYear: [utcYear, 2],

const stackOffsets = {
stackOffsetExpand: stackOffsetExpand,
stackOffsetNone: stackOffsetNone,
stackOffsetSilhouette: stackOffsetSilhouette,
stackOffsetWiggle: stackOffsetWiggle
}
const stackOrders = {
stackOrderAscending: stackOrderAscending,
stackOrderDescending: stackOrderDescending,
stackOrderInsideOut: stackOrderInsideOut,
stackOrderNone: stackOrderNone,
stackOrderReverse: stackOrderReverse
}
// If localtime, the dates are assumed to be boundaries in localtime
export function timeMultiFormat(localtime, tf) {
let second = utcSecond,
minute = utcMinute,
hour = utcHour,
day = utcDay,
week = utcWeek,
month = utcMonth,
year = utcYear;
if (localtime === true) {
second = timeSecond;
minute = timeMinute;
hour = timeHour;
day = timeDay;
week = timeWeek;
month = timeMonth;
year = timeYear;
}
if (tf == null) tf = timeFormat;
return function (date, i) {
let formatMillisecond = tf(".%L"),
formatSecond = tf(":%S"),
formatMinute = tf("%I:%M"),
formatHour = tf("%I %p"),
formatDay = tf("%a %d"),
formatWeek = tf("%b %d"),
formatMonth = tf("%B"),
formatYear = tf("%Y"),
formatShortYear = tf("%y");
return (second(date) < date ? formatMillisecond
: minute(date) < date ? formatSecond
: hour(date) < date ? formatMinute
: day(date) < date ? formatHour
: month(date) < date ? (week(date) < date ? formatDay : formatWeek)
: year(date) < date ? formatMonth
: (i === 0 || date.getUTCFullYear() % 100 === 0) ? formatYear
: formatShortYear)(date);
}
}
const DEFAULT_SIZE = 420;
const DEFAULT_ASPECT = 160 / 420;
const DEFAULT_MARGIN = 40; // white space
const DEFAULT_MARGIN = 26; // white space
const DEFAULT_INSET = 24; // scale space
const DEFAULT_TICK_FORMAT_VALUE = ',.0f';
const DEFAULT_TICK_FORMAT_VALUE_SI = '.2s';
const DEFAULT_TICK_FORMAT_VALUE_SMALL = '.3f';
const DEFAULT_TICK_COUNT = 4;
const DEFAULT_SYMBOL_SIZE = 32;
const DEFAULT_SCALE = 42; // why not
const DEFAULT_LEGEND_SIZE = 10;
const DEFAULT_LEGEND_PADDING_X = 8;
const DEFAULT_LEGEND_PADDING_Y = 24;
const DEFAULT_LEGEND_TEXT_SCALE = 8; // hack value to do fast estimation of length of string
const DEFAULT_AXIS_PADDING = 8;
const DEFAULT_MAJOR_TICK_SIZE = 8;
const DEFAULT_MINOR_TICK_SIZE = 4;
const DEFAULT_FILL_OPACITY = 0.33;
const DEFAULT_TIP_CIRCLE_SIZE = 4;
const DEFAULT_TIP_OFFSET = 4;
// Font fallback chosen to keep presentation on places like GitHub where Content Security Policy prevents inline SRC
const DEFAULT_STYLE = [ "@import url(https://fonts.googleapis.com/css?family=Source+Code+Pro:300);",
"text{ font-family: 'Source Code Pro', Consolas, 'Liberation Mono', Menlo, Courier, monospace; font-weight: 300; fill: " + display.text.black + "; }",
const DEFAULT_STYLE = [ "@import url(https://fonts.googleapis.com/css?family=Source+Code+Pro:300,500); @import 'https://fonts.googleapis.com/css?family=Raleway:400,500';",
".axis text{ font-family: 'Source Code Pro', Consolas, 'Liberation Mono', Menlo, Courier, monospace; font-weight: 300; fill: " + display.text.black + "; }",
".voronoi text{ font-size: 14px; font-family: 'Raleway', sans-serif; font-weight: 500 }",
".chart-legends text{ font-size: 14px; font-family: 'Raleway', sans-serif; font-weight: 300; fill: " + display.text.black + "; }",
".axis path, .axis line { fill: none; stroke: " + display.lines.seperator + "; shape-rendering: crispEdges; }",
"g.axis-v path { stroke: none }",
"g.axis-v-minor path { stroke: none }",
"g.axis-i path, g.axis-i g.tick line, g.axis-i-minor g.tick line { stroke-width: 1.0px; stroke: " + display.text.black + " }",
"g.axis-i-minor path { stroke: none }",
"line { stroke-width: 1.5px }",
"line.grid { stroke-width: 1.0px }",
"line.grid, g.axis-i g.tick line.grid { stroke-width: 2.0px; stroke-dasharray: 2,2; stroke: " + display.lines.seperator + " }",
".legend text { font-size: 12px }",
"path.area { opacity: 0.33 }",
".highlight { opacity: 0.66 }",
".highlight text { font-size: 12px }"
"path.stroke { stroke-width: 2.5px }",
/* "path.area { opacity: 0.33 }", */
".voronoi path { stroke: none }"
].join(' \n');
/*
"path.series-0 { stroke: red }",
"path.series-1 { stroke: green }",
"path.series-2 { stroke: orange }",
"path.series-3 { stroke: grey }"
*/
export default function lines(id) {

@@ -183,3 +270,3 @@ let classed = 'chart-lines',

height = null,
margin = { top: 0, left: DEFAULT_MARGIN, bottom: DEFAULT_MARGIN, right: 0 },
margin = DEFAULT_MARGIN,
style = DEFAULT_STYLE,

@@ -192,4 +279,5 @@ scale = 1.0,

maxIndex = null,
inset = DEFAULT_INSET,
inset = null,
tickFormatValue = null,
tickFormatIndex = null,
tickDisplayValue = null,

@@ -199,2 +287,4 @@ tickDisplayIndex = null,

tickCountIndex = null,
tickMinorValue = null,
tickMinorIndex = null,
niceValue = true,

@@ -204,3 +294,20 @@ niceIndex = true,

gridIndex = false,
fillOpacity = null,
fillArea = null,
fillStroke = null,
language = null,
stacked = null,
stackOrder = stackOrderNone,
stackOffset = stackOffsetNone,
voronoiAttraction = 0.33,
animateAxis = true,
animateLabels = true,
axisValue = 'left',
animation = null,
tipHtml = null,
trim = null,
_ptrim = null,
legendOrientation = 'bottom',
axisPaddingIndex = DEFAULT_AXIS_PADDING,
axisPaddingValue = DEFAULT_AXIS_PADDING,
legend = [ ],

@@ -214,3 +321,3 @@ fill = null,

displayTip = -1,
value = function (d, i) {
value = function (d, i, notArray) {
if (Array.isArray(d)) {

@@ -224,5 +331,9 @@ return d;

return [ i, Array.isArray(d) ? d : [ d ] ];
return [ i, (notArray === true || Array.isArray(d)) ? d : [ d ] ];
};
let tid = null;
if (id) tid = 'tip-' + id;
let rtip = tip(tid).offset([- DEFAULT_TIP_CIRCLE_SIZE - DEFAULT_TIP_OFFSET, 0]);
// [ [ [i1,v1], [i1,v1] ], [ [i2,v2] ... ], ... ]

@@ -264,6 +375,7 @@ function _flatArrays(a) {

if (c == null) return null;
// TODO: does not seem to behave
if (typeof c === 'string') {
return map[c];
}
return c;

@@ -275,4 +387,21 @@ }

let _mapSymbols = _map(symbols);
let _mapIntervalTickCount = _map(intervals);
let _fnIntervals = _map(intervals);
let _mapStackOffset = _map(stackOffsets);
let _mapStackOrder = _map(stackOrders);
let _mapIntervalTickCount = function (c) {
let o = _fnIntervals(c);
if (o == null) {
return [];
}
if (!Array.isArray(o)) {
return [ c ];
}
return o;
}
function _makeFillFn() {

@@ -294,31 +423,24 @@ let colors = () => fill;

transition = (context.selection !== undefined);
formatDefaultLocale(units(language).d3);
if (labelTime != null) {
timeFormatDefaultLocale(time(language).d3);
}
let localeFormat = units(language).d3;
formatDefaultLocale(localeFormat);
let defaultValueFormat = format(DEFAULT_TICK_FORMAT_VALUE);
let defaultValueFormatSi = format(DEFAULT_TICK_FORMAT_VALUE_SI);
let defaultValueFormatSmall = format(DEFAULT_TICK_FORMAT_VALUE_SMALL);
let localeTime = time(language).d3;
timeFormatDefaultLocale(localeTime);
let scaleFn = tickDisplayValue;
if (scaleFn == null && logValue === 0) {
if (tickFormatValue != null) {
let fn = format(tickFormatValue);
scaleFn = (i) => fn(i);
} else {
scaleFn = function (i) {
if (i === 0.0) {
return defaultValueFormat(i);
} else if (i > 9999 || i <= 0.001) {
return defaultValueFormatSi(i);
} else if (i < 1) {
return defaultValueFormatSmall(i);
} else {
return defaultValueFormat(i);
}
}
}
let _fillOpacity = fillOpacity,
_fillArea = fillArea,
_fillStroke = fillStroke;
if (stacked !== null && stacked !== false) {
if (_fillOpacity == null) _fillOpacity = 1.0;
if (_fillArea == null) _fillArea = true;
if (_fillStroke == null) _fillStroke = false;
} else {
if (_fillOpacity == null) _fillOpacity = DEFAULT_FILL_OPACITY;
if (_fillArea == null) _fillArea = true;
if (_fillStroke == null) _fillStroke = true;
}

@@ -341,9 +463,15 @@ selection.each(function() {

// Tip
let tid = null;
if (id) tid = 'tip-' + id;
let rtip = tip(tid).html((d) => d);
let st = style + ' ' + rtip.style();
rtip.style(st);
elmS.call(rtip);
let _inset = inset;
if (_inset == null) {
_inset = { top: 0, bottom: DEFAULT_INSET + DEFAULT_MAJOR_TICK_SIZE, left: 0, right: 0 };
if (axisValue === 'left') {
_inset.left = DEFAULT_INSET;
} else {
_inset.right = DEFAULT_INSET;
}
} else if (typeof _inset === 'object') {
_inset = { top: _inset.top, bottom: _inset.bottom, left: _inset.left, right: _inset.right };
} else {
_inset = { top: _inset, bottom: _inset, left: _inset, right: _inset };
}

@@ -354,17 +482,80 @@ // Create required elements

g = elmS.append('g').attr('class', classed).attr('id', id);
g.append('g').attr('class', 'axis-v-minor axis');
g.append('g').attr('class', 'axis-v axis');
g.append('g').attr('class', 'axis-i-minor axis');
g.append('g').attr('class', 'axis-i axis');
g.append('g').attr('class', 'legend');
g.append('g').attr('class', 'lines');
g.append('g').attr('class', 'voronoi');
}
g.selectAll('circle.tip-point').remove();
let data = g.datum() || [];
let vdata = _flatArrays(data.map((d, i) => value(d, i)));
if (!Array.isArray(data)) {
data = [ data ];
}
g.datum(vdata); // this rebind is required even though there is a following select
if (data.length > 0) {
if (stacked !== null && stacked !== false) {
let stacker = stack();
if (stacked === true) {
stacker.keys(data[0].v.map((d,i) => i)).value((d,k) => d.v[k]);
} else {
stacker.keys(stacked);
}
let _stackOrder = _mapStackOrder(stackOrder);
if (_stackOrder != null) stacker.order(_stackOrder);
let _stackOffset = _mapStackOffset(stackOffset);
if (_stackOffset != null) stacker.offset(_stackOffset);
data = stacker(data).map(s => s.map(v => [ v.data.l, v ]));
} else if (Array.isArray(data[0])) {
data = data.map(a => a.map((d, i) => value(d, i, true))).map(s => s.map(e => [ e[0], [ 0, e[1] ] ]) );
} else {
data = _flatArrays(data.map((d, i) => value(d, i, false))).map(s => s.map(e => [ e[0], [ 0, e[1] ] ]) );
}
}
let _mapData = function(option) {
let out = [];
if (!Array.isArray(option)) {
out = data.map(d => option === true ?
trim == null ? d : d.slice(0, trim)
: []);
} else {
out = data.map(function (d, i) {
if (i < option.length) {
return option[i] === true ?
trim == null ? d : d.slice(0, trim)
: [];
}
return [];
});
}
return out;
}
let fdata = _mapData(_fillArea);
let sdata = _mapData(_fillStroke);
let _curve = data.map(function (d, i) {
if (!Array.isArray(curve)) {
return _mapCurve(curve);
}
if (i < curve.length) {
return _mapCurve(curve[i]);
}
return null;
});
g.datum(data); // this rebind is required even though there is a following select
let minV = minValue;
if (minV == null) {
minV = min(vdata, d => min(d, d1 => d1[1]));
minV = min(data, d => min(d, d1 => min(d1[1]) ));
if (minV > 0) {

@@ -377,3 +568,3 @@ minV = logValue === 0 ? 0 : 1;

if (maxV == null) {
maxV = max(vdata, d => max(d, d1 => d1[1]));
maxV = max(data, d => max(d, d1 => max(d1[1]) ));
}

@@ -383,3 +574,3 @@

if (minI == null) {
minI = min(vdata, d => min(d, d1 => d1[0]));
minI = min(data, d => min(d, d1 => d1[0]));
}

@@ -390,3 +581,3 @@ if (minI == null) minI = 0;

if (maxI == null) {
maxI = max(vdata, d => max(d, d1 => d1[0]));
maxI = max(data, d => max(d, d1 => d1[0]));
}

@@ -397,33 +588,12 @@ if (maxI == null) maxI = DEFAULT_SCALE;

h = root.childHeight();
let colors = _makeFillFn();
// Create the legend
if (legend.length > 0) {
h = h - (DEFAULT_LEGEND_SIZE + DEFAULT_LEGEND_PADDING_Y);
let rg = g.select('g.legend');
let lg = rg.attr('transform', 'translate(' + (w/2) + ',' + (h + DEFAULT_LEGEND_PADDING_Y) + ')').selectAll('g').data(legend);
lg.exit().remove();
let newlg = lg.enter().append('g');
let colors = _makeFillFn();
if (legend.length > 0 && legendOrientation !== 'voronoi') {
let lchart = legends().width(w).height(h).inset(0).fill(colors).orientation(legendOrientation).spacing(7);
newlg.append('rect')
.attr('width', DEFAULT_LEGEND_SIZE)
.attr('height', DEFAULT_LEGEND_SIZE)
.attr('fill', colors);
_inset = lchart.childInset(_inset);
newlg.append('text')
.attr('dominant-baseline', 'central')
.attr('y', DEFAULT_LEGEND_SIZE / 2)
.attr('x', () => DEFAULT_LEGEND_SIZE + DEFAULT_LEGEND_PADDING_X);
lg = newlg.merge(lg);
lg.selectAll('text').text((d) => d);
let lens = legend.map((s) => s.length * DEFAULT_LEGEND_TEXT_SCALE + DEFAULT_LEGEND_SIZE + 2 * DEFAULT_LEGEND_PADDING_X);
let clens = []
let total = lens.reduce((p, c) => (clens.push(p) , p + c), 0);
let offset = -total / 2;
rg.selectAll('g').data(clens).attr('transform', (d) => 'translate(' + (offset + d) + ',0)');
elmS.datum(legend).call(lchart);
}

@@ -433,3 +603,3 @@

if (logValue > 0) sV = scaleLog().base(logValue);
let scaleV = sV.domain([ minV, maxV ]).range([ h, inset ]);
let scaleV = sV.domain([ minV, maxV ]).range([ h - _inset.bottom, _inset.top ]);
if (niceValue === true) {

@@ -442,20 +612,38 @@ scaleV = scaleV.nice();

let domainI = [ minI, maxI ];
let scaleI = sI.domain(domainI).range([ 0, w - inset ]);
let scaleI = sI.domain(domainI).range([ _inset.left, w - _inset.right ]);
if (niceIndex === true) {
scaleI = scaleI.nice();
}
let aV = axisLeft(scaleV).ticks(tickCountValue, (tickFormatValue == null ? DEFAULT_TICK_FORMAT_VALUE : tickFormatValue));
let formatValue = tickFormatValue;
if (logValue > 0 && formatValue == null) {
formatValue = '.0r';
}
let axis = (axisValue === 'left') ? axisLeft : axisRight;
let aV = axis(scaleV)
.tickPadding(axisPaddingValue)
.ticks(tickCountValue, (formatValue == null ? scaleV.tickFormat(tickCountValue) : formatValue));
if (gridValue === true) {
aV.tickSizeInner(inset - w);
aV.tickSizeInner((_inset.left + _inset.right) - w);
} else {
aV.tickSizeInner(DEFAULT_MAJOR_TICK_SIZE);
}
aV.tickFormat(scaleFn);
if (tickDisplayValue) {
aV.tickFormat(tickDisplayValue);
}
let aVMinor = null;
if (tickMinorValue !== null) {
aVMinor = axis(scaleV).ticks(tickMinorValue).tickSizeInner(DEFAULT_MINOR_TICK_SIZE).tickFormat(() => undefined);
}
let axisTranslate = (axisValue === 'left') ? _inset.left : w - _inset.right;
let gAxisV = g.select('g.axis-v')
.attr('transform', 'translate(' + axisTranslate + ',0)');
let gAxisVMinor = g.select('g.axis-v-minor')
.attr('transform', 'translate(' + axisTranslate + ',0)');
g.select('g.axis-v')
.attr('transform', 'translate(0,0)')
.call(aV)
.selectAll('line')
.attr('class', gridValue ? 'grid' : null);
let aI = axisBottom(scaleI);
let aI = axisBottom(scaleI).tickPadding(axisPaddingIndex);
if (labelTime != null) {

@@ -466,44 +654,81 @@ let freq = _mapIntervalTickCount(tickCountIndex);

}
aI.tickFormat(timeFormat(labelTime));
if (typeof labelTime === 'function') {
aI.tickFormat(labelTime);
} else if (labelTime === 'multi') {
aI.tickFormat(timeMultiFormat());
} else {
aI.tickFormat(timeFormat(labelTime));
}
} else {
if (tickCountIndex != null) {
aI = aI.ticks(tickCountIndex);
}
aI = aI.ticks(tickCountIndex, (tickFormatIndex == null ? scaleI.tickFormat(tickCountIndex) : tickFormatIndex));
}
if (gridIndex === true) {
aI.tickSizeInner(inset - h);
aI.tickSizeInner((_inset.top + _inset.bottom) - h);
} else {
aI.tickSizeInner(DEFAULT_MAJOR_TICK_SIZE);
}
if (tickDisplayIndex != null) {
aI.tickFormat(i => tickDisplayIndex(i));
aI.tickFormat(tickDisplayIndex);
}
g.select('g.axis-i')
.attr('transform', 'translate(0,' + h + ')')
.call(aI)
let aIMinor = null;
if (tickMinorIndex !== null) {
let density = tickMinorIndex;
if (labelTime != null) {
density = _mapIntervalTickCount(tickMinorIndex);
}
aIMinor = axisBottom(scaleI).tickArguments(density).tickSizeInner(DEFAULT_MINOR_TICK_SIZE).tickFormat(() => undefined);
}
let gAxisI = g.select('g.axis-i')
.attr('transform', 'translate(0,' + (h - _inset.bottom) + ')');
let gAxisIMinor = g.select('g.axis-i-minor')
.attr('transform', 'translate(0,' + (h - _inset.bottom) + ')');
if (transition === true && animateAxis === true) {
gAxisVMinor = gAxisVMinor.transition(context);
gAxisIMinor = gAxisIMinor.transition(context);
gAxisV = gAxisV.transition(context);
gAxisI = gAxisI.transition(context);
}
if (aIMinor !== null) {
gAxisIMinor.call(aIMinor);
} else {
gAxisIMinor.selectAll('*').remove();
}
if (aVMinor !== null) {
gAxisVMinor.call(aVMinor);
} else {
gAxisVMinor.selectAll('*').remove();
}
gAxisV.call(aV)
.selectAll('line')
.attr('class', gridValue ? 'grid' : null);
gAxisI.call(aI)
.selectAll('line')
.attr('class', gridIndex ? 'grid' : null);
// Note: A lot of scaleI, scaleV calls.
let lines = line()
.x(d => scaleI(d[0]))
.y(d => scaleV(d[1]));
.y(d => scaleV(d[1][1]))
.defined(d => scaleI(d[0]) <= (w - _inset.right) && scaleV(d[1][1]) >= _inset.top);
let areas = area()
.x(d => scaleI(d[0]))
.y0(h)
.y1(d => scaleV(d[1]));
.y0(d => scaleV(d[1][0])) // bottom
.y1(d => scaleV(d[1][1])) // top
.defined(d => scaleI(d[0]) <= (w - _inset.right) && scaleV(d[1][1]) >= _inset.top);
let cv = _mapCurve(curve);
if (cv != null) {
lines.curve(cv);
areas.curve(cv);
}
let colors = _makeFillFn();
let uS = psymbol.map(_mapSymbols).map(s => s != null ? symbol().type(s).size(symbolSize) : null);
let sym = vdata.map((d, i) => i < uS.length ? uS[i] : null).map(d => d !== null ? d : null);
let sym = data.map((d, i) => i < uS.length ? uS[i] : null).map(d => d !== null ? d : null);
let elmL = g.select('g.lines');
let elmG = elmL.selectAll('g.line').data(vdata);
let elmG = elmL.selectAll('g.line').data(data);
elmG.exit().remove();

@@ -516,18 +741,267 @@ let elmGNew = elmG.enter().append('g').attr('class', 'line');

let elmArea = elmL.selectAll('path.area').data(vdata);
elmArea.attr('d', areas)
.attr('fill', colors);
let elmArea = elmL.selectAll('path.area').data(fdata);
let elmStroke = elmL.selectAll('path.stroke').data(sdata);
let elmStroke = elmL.selectAll('path.stroke').data(vdata);
elmStroke.attr('d', lines)
.attr('stroke', colors);
if (transition === true) {
elmArea = elmArea.transition(context);
elmStroke = elmStroke.transition(context);
}
function revealInterpolation(tr, fn) {
return function (d, i) {
let ln = fn(i);
if (tr == null) tr = 1;
let interpolate = scaleLinear()
.domain([0, 1])
.range([tr, d.length + 1]);
return function(t) {
if (d.length == 0) return '';
let flooredX = Math.floor(interpolate(t));
let weight = interpolate(t) - flooredX;
let interpolatedLine = d.slice(0, flooredX);
if (flooredX > 0 && flooredX < d.length) {
let wY0 = d[flooredX][1][0] * weight + d[flooredX-1][1][0] * (1.0 - weight);
let wY1 = d[flooredX][1][1] * weight + d[flooredX-1][1][1] * (1.0 - weight);
let wX = d[flooredX][0] * weight + d[flooredX-1][0] * (1.0 - weight);
interpolatedLine.push([ wX, [ wY0, wY1 ] ]);
}
return ln(interpolatedLine);
}
}
}
function valueInterpolation(tr, fn) {
return function (d, i) {
let ln = fn(i);
return function(t) {
let flooredX = d.length - 1;
if (flooredX < 0) return '';
let interpolatedLine = d.slice(0, flooredX);
let wY0 = d[flooredX][1][0] * t;
let wY1 = d[flooredX][1][1] * t;
interpolatedLine.push([ d[flooredX][0], [ wY0, wY1 ] ]);
return ln(interpolatedLine);
}
}
}
elmArea.attr('opacity', _fillOpacity)
.attr('fill', (d,i) => colors(d, i, 'area'));
elmStroke.attr('stroke', (d,i) => colors(d, i, 'stroke'));
let interpolation = null;
if (transition === true) {
if (animation === 'reveal') {
interpolation = revealInterpolation;
} else if (animation === 'value') {
interpolation = valueInterpolation;
}
}
if (interpolation !== null) {
elmArea.attrTween('d', interpolation(_ptrim, i => _curve[i] != null ? areas.curve(_curve[i]) : areas));
elmStroke.attrTween('d', interpolation(_ptrim, i => _curve[i] != null ? lines.curve(_curve[i]) : lines));
} else {
elmArea.attr('d', (d, i) => _curve[i] != null ? areas.curve(_curve[i])(d, i) : areas(d, i));
elmStroke.attr('d', (d, i) => _curve[i] != null ? lines.curve(_curve[i])(d, i) : lines(d, i));
}
let eS = elmG.select('g.symbols').selectAll('path').data((d, i) => sym[i] != null ? d.map(function (v) { return { v : v, i : i }; }) : []);
eS.exit().remove();
eS = eS.enter().append('path').merge(eS);
eS.attr('transform', d => 'translate('+scaleI(d.v[0])+','+scaleV(d.v[1])+')')
eS.attr('transform', d => 'translate('+scaleI(d.v[0])+','+scaleV(d.v[1][1])+')')
.attr('d', (d) => sym[d.i](d.v, d.i))
.attr('fill', d => colors(d.v, d.i))
.attr('fill', d => colors(d.v, d.i, 'symbol'))
.attr('stroke', 'none');
let flat = data.reduce((p, a, s) => p.concat(a.map((e, i) => [ e[0], e[1][1], s, i ] )), []);
let overlay = voronoi()
.x(d => scaleI(d[0]))
.y(d => scaleV(d[1]))
.extent([ [ _inset.left, _inset.top ], [ w - _inset.right, h - _inset.bottom ] ])
.polygons(flat);
let vmesh = g.select('g.voronoi').selectAll('path').data(overlay);
vmesh.exit().remove();
vmesh = vmesh.enter().append('path')
.attr('fill', 'none')
.style('pointer-events', 'all')
.merge(vmesh);
vmesh.attr('d', d => d != null ? 'M' + d.join('L') + 'Z' : '')
.attr('class', d => d != null ? 'series-' + d.data[2] : null);
let _tipHtml = tipHtml;
if (_tipHtml == null) {
let fmtX = null;
if (labelTime != null) {
if (typeof labelTime === 'function') {
fmtX = labelTime
} else if (labelTime === 'multi') {
let tf = timeFormatLocale(localeTime).format;
fmtX = timeMultiFormat(false, tf);
} else {
let tf = timeFormatLocale(localeTime);
fmtX = tf.format(labelTime);
}
} else if (tickFormatIndex != null) {
fmtX = formatLocale(localeFormat).format(tickFormatIndex);
}
let fmtY = null;
if (formatValue != null) {
if (typeof formatValue === 'function') {
fmtY = formatValue;
} else {
fmtY = formatLocale(localeFormat).format(formatValue);
}
}
_tipHtml = function (d,i,s) {
let v = value(d);
let x = v[0];
let y = v[1][s];
if (fmtX != null) {
x = fmtX(x);
}
if (fmtY != null) {
y = fmtY(y);
}
return x + ', ' + y;
}
}
// Tip
let st = style + ' ' + rtip.style();
rtip.style(st);
rtip.html(_tipHtml);
elmS.call(rtip);
vmesh.on('mouseover', function (d) {
let s = d.data[2];
let i = d.data[3];
let item = data[s][i];
let y = scaleV(item[1][1]);
let x = scaleI(item[0]);
let nested = item[1].data;
if (nested !== undefined) {
item = nested;
}
g.append('circle')
.attr('r', DEFAULT_TIP_CIRCLE_SIZE)
.attr('class', 'tip outline')
.attr('cx', x)
.attr('cy', y)
.attr('fill', 'black');
let circle = g.append('circle')
.attr('r', DEFAULT_TIP_CIRCLE_SIZE - 0.5)
.attr('class', 'tip fill')
.attr('cx', x)
.attr('cy', y)
.attr('fill', colors(item, s));
rtip.show.apply(circle.node(), [ item, i, s ]);
});
elmS.on('mouseout', function () {
g.selectAll('circle.tip').remove();
rtip.hide.apply(this);
});
rtip.hide();
// highlight selected entry
// .attr('fill', (d, i) => (candidates.indexOf(i) === -1) ? 'none' : 'red')
let labels = [];
if (legendOrientation === 'voronoi') {
const centerI = (scaleI.range()[1] - scaleI.range()[0]) / 2;
const UNIT_TO_RAD = Math.PI / 2;
let calculatePolygon = function(d, i) {
let c = polygonCentroid(d);
// the more central (in x), the more suitable
let centraility = Math.cos(UNIT_TO_RAD * Math.abs(centerI - c[0]) / centerI);
// a: larger number, more suitable polygon
return { a: polygonArea(d) * centraility, s: d.data[2], i: i, c: c };
}
// will drag the text position towards the data point by a funciton of
// voronoiAttraction
let calculateTextPosition = function(centroid, point) {
let angle = Math.atan2(centroid[1] - point[1], centroid[0] - point[0]);
let x = centroid[0] - point[0];
let y = centroid[1] - point[1];
let l = Math.sqrt(x*x + y*y);
return [ centroid[0] - voronoiAttraction*l*Math.cos(angle), centroid[1] - voronoiAttraction*l*Math.sin(angle) ];
}
let polys = overlay.map(calculatePolygon);
let candidates = nest()
.key(d => d != null ? d.s : '')
.sortValues((a,b) => descending(a.a, b.a))
.entries(polys)
.filter(d => d.key !== '')
.map(function (d) {
for (let i=0; i < d.values.length; i++) {
let e = d.values[i];
if (e != null) return e.i;
}
// nothing was an option
return 0;
});
labels = candidates.map(i => calculateTextPosition(polys[i].c, [ scaleI(flat[i][0]), scaleV(flat[i][1]) ]));
}
let vlabels = g.select('g.voronoi').selectAll('text').data(labels);
vlabels.exit().remove();
vlabels = vlabels.enter().append('text')
.attr('text-anchor', 'middle')
.attr('dominant-baseline', 'central')
.merge(vlabels);
if (transition === true && animateLabels === true) {
vlabels = vlabels.transition(context);
}
vlabels.attr('x', d => d[0])
.attr('y', d => d[1])
.attr('fill', (d,i) => colors(d,i,'legend'))
.text((d, i) => i < legend.length ? legend[i] : '');
_ptrim = trim;
});

@@ -602,2 +1076,6 @@

};
_impl.tickFormatIndex = function(value) {
return arguments.length ? (tickFormatIndex = value, _impl) : tickFormatIndex;
};

@@ -611,3 +1089,19 @@ _impl.tickDisplayValue = function(value) {

};
_impl.tickCountValue = function(value) {
return arguments.length ? (tickCountValue = value, _impl) : tickCountValue;
};
_impl.tickCountIndex = function(value) {
return arguments.length ? (tickCountIndex = value, _impl) : tickCountIndex;
};
_impl.tickMinorValue = function(value) {
return arguments.length ? (tickMinorValue = value, _impl) : tickMinorValue;
};
_impl.tickMinorIndex = function(value) {
return arguments.length ? (tickMinorIndex = value, _impl) : tickMinorIndex;
};
_impl.style = function(value) {

@@ -633,10 +1127,2 @@ return arguments.length ? (style = value, _impl) : style;

_impl.tickCountValue = function(value) {
return arguments.length ? (tickCountValue = value, _impl) : tickCountValue;
};
_impl.tickCountIndex = function(value) {
return arguments.length ? (tickCountIndex = value, _impl) : tickCountIndex;
};
_impl.displayTip = function(value) {

@@ -678,7 +1164,72 @@ return arguments.length ? (displayTip = value, _impl) : displayTip;

_impl.stacked = function(value) {
return arguments.length ? (stacked = value, _impl) : stacked;
};
_impl.stackOrder = function(value) {
return arguments.length ? (stackOrder = value, _impl) : stackOrder;
};
_impl.stackOffset = function(value) {
return arguments.length ? (stackOffset = value, _impl) : stackOffset;
};
_impl.trim = function(value) {
return arguments.length ? (trim = value, _impl) : trim;
};
_impl.fill = function(value) {
return arguments.length ? (fill = value, _impl) : fill;
};
_impl.fillArea = function(value) {
return arguments.length ? (fillArea = value, _impl) : fillArea;
};
_impl.fillStroke = function(value) {
return arguments.length ? (fillStroke = value, _impl) : fillStroke;
};
_impl.fillAreaOpacity = function(value) {
return arguments.length ? (fillOpacity = value, _impl) : fillOpacity;
};
_impl.axisValue = function(value) {
return arguments.length ? (axisValue = value, _impl) : axisValue;
};
_impl.axisPaddingIndex = function(value) {
return arguments.length ? (axisPaddingIndex = value, _impl) : axisPaddingIndex;
};
_impl.axisPaddingValue = function(value) {
return arguments.length ? (axisPaddingValue = value, _impl) : axisPaddingValue;
};
_impl.legendOrientation = function(value) {
return arguments.length ? (legendOrientation = value, _impl) : legendOrientation;
};
_impl.voronoiAttraction = function(value) {
return arguments.length ? (voronoiAttraction = value, _impl) : voronoiAttraction;
};
_impl.animateAxis = function(value) {
return arguments.length ? (animateAxis = value, _impl) : animateAxis;
};
_impl.animateLabels = function(value) {
return arguments.length ? (animateLabels = value, _impl) : animateLabels;
};
_impl.animation = function(value) {
return arguments.length ? (animation = value, _impl) : animation;
};
_impl.tipHtml = function(value) {
return arguments.length ? (tipHtml = value, _impl) : tipHtml;
};
return _impl;
}

@@ -13,4 +13,4 @@ var tape = require("@redsift/tape-reel")("<div id='test'></div>"),

// should have an X and Y axis
t.equal(el.selectAll('g.axis').size(), 2);
// should have an X and Y major and minor axis
t.equal(el.selectAll('g.axis').size(), 4);

@@ -21,1 +21,81 @@ t.end();

tape("html() 1 line state", function(t) {
var host = lines.html();
var el = d3.select('#test');
el.datum([ 1, 2 ]).call(host);
t.equal(el.selectAll('svg').size(), 1);
var node = el.select(host.self());
// In this chart, one path should be there
t.equal(node.selectAll('path.stroke').size(), 1);
t.equal(node.selectAll('path.area').size(), 1);
t.end();
});
tape("html() 2 line state", function(t) {
var host = lines.html();
var el = d3.select('#test');
el.datum([ [ 0, 1 ], [ 1, 2 ] ]).call(host);
t.equal(el.selectAll('svg').size(), 1);
var node = el.select(host.self());
// In this chart, one path should be there
t.equal(node.selectAll('path.stroke').size(), 2);
t.equal(node.selectAll('path.area').size(), 2);
t.equal(node.selectAll('.voronoi path.series-0').size(), 2);
t.equal(node.selectAll('.voronoi path.series-1').size(), 2);
t.end();
});
[ null, 'top', 'left' , 'right', 'bottom', 'voronoi' ].forEach(function (o) {
tape("html() data reentrant", function(t) {
var data = [ [ 0, 1 ], [ 1, 2 ] ];
var host = lines.html();
if (o != null) {
host.legendOrientation(o);
}
var el = d3.select('#test');
el.datum(data).call(host);
t.equal(el.selectAll('svg').size(), 1);
var initial = el.selectAll('*').size();
el.datum(data).call(host);
t.equal(initial, el.selectAll('*').size());
t.end();
});
});
tape("html() voronoi updates", function(t) {
var host = lines.html().legendOrientation('voronoi');
var el = d3.select('#test');
el.datum([ [ 0, 1 ], [ 1, 2 ] ]).call(host);
t.equal(el.selectAll('svg').size(), 1);
t.equal(el.selectAll('.voronoi path').size(), 4, '4 voronoi polygons');
t.equal(el.selectAll('.voronoi text').size(), 2, '2 text labels');
el.datum([ [ 0, 1 ] ]).call(host);
t.equal(el.selectAll('.voronoi path').size(), 2, '2 voronoi polygons');
t.equal(el.selectAll('.voronoi text').size(), 1, '1 text label');
t.end();
});

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc