@redsift/d3-rs-lines
Advanced tools
Comparing version 0.0.2 to 0.0.3
@@ -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 |
897
src/lines.js
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
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Long strings
Supply chain riskContains long string literals, which may be a sign of obfuscated or packed code.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
1220796
18
9807
80
15
8
+ Addedd3-polygon@^1.0.0
+ Addedd3-voronoi@^1.0.1
+ Added@redsift/d3-rs-legends@0.0.4(transitive)
+ Addedd3-polygon@1.0.6(transitive)
+ Addedd3-voronoi@1.1.4(transitive)
Updated@redsift/d3-rs-svg@~0.2.5
Updatedd3-scale@^1.0.1