Comparing version 0.6.0 to 0.7.0
{ | ||
"name": "mapd3", | ||
"version": "0.6.0", | ||
"version": "0.7.0", | ||
"description": "A fast chart library for the fastest DB", | ||
@@ -5,0 +5,0 @@ "main": "dist/mapd3.js", |
@@ -84,2 +84,7 @@ import * as d3 from "./helpers/d3-service" | ||
cache.xAxis.tickValues(scales.xScale.domain().filter((d, i) => !(i % config.xTickSkip))) | ||
} else if (config.keyType === "number") { | ||
if (config.xAxisFormat && config.xAxisFormat !== "auto") { | ||
const formatter = d3.format(config.xAxisFormat) | ||
cache.xAxis.tickFormat(formatter) | ||
} | ||
} | ||
@@ -86,0 +91,0 @@ |
import {keys} from "./helpers/constants" | ||
import {override} from "./helpers/common" | ||
export default function Bar (config, cache) { | ||
export default function Bar (_container) { | ||
const getColor = (d) => cache.colorScale(d[keys.ID]) | ||
let config = { | ||
margin: { | ||
top: 60, | ||
right: 30, | ||
bottom: 40, | ||
left: 70 | ||
}, | ||
width: 800, | ||
height: 500, | ||
chartType: null | ||
} | ||
let scales = { | ||
colorScale: null, | ||
xScale: null, | ||
yScale: null, | ||
yScale2: null | ||
} | ||
const cache = { | ||
container: _container, | ||
svg: null, | ||
chartHeight: null | ||
} | ||
let data = { | ||
dataBySeries: null, | ||
groupKeys: null, | ||
stack: null, | ||
stackData: null | ||
} | ||
const getColor = (d) => scales.colorScale(d[keys.ID]) | ||
function buildSVG () { | ||
cache.chartWidth = config.width - config.margin.left - config.margin.right | ||
cache.chartHeight = config.height - config.margin.top - config.margin.bottom | ||
if (!cache.svg) { | ||
cache.svg = cache.container.append("g") | ||
.classed("mark-group", true) | ||
} | ||
} | ||
function drawBars () { | ||
const bars = cache.svg.select(".chart-group") | ||
.selectAll(".mark") | ||
.data(cache.dataBySeries[0].values) | ||
const bars = cache.svg.selectAll(".mark") | ||
.data(data.dataBySeries[0].values) | ||
bars.enter() | ||
.append("rect") | ||
.attr("class", () => ["mark", "rect"].join(" ")) | ||
.attr("class", "mark rect") | ||
.merge(bars) | ||
.attr("x", (d) => cache.xScale(d[keys.DATA])) | ||
.attr("y", (d) => cache.yScale(d[keys.VALUE])) | ||
.attr("width", () => cache.xScale.bandwidth()) | ||
.attr("height", (d) => cache.chartHeight - cache.yScale(d[keys.VALUE])) | ||
.attr("x", (d) => scales.xScale(d[keys.DATA])) | ||
.attr("y", (d) => scales.yScale(d[keys.VALUE])) | ||
.attr("width", () => scales.xScale.bandwidth()) | ||
.attr("height", (d) => cache.chartHeight - scales.yScale(d[keys.VALUE])) | ||
.style("stroke", "white") | ||
@@ -28,11 +70,10 @@ .style("fill", getColor) | ||
const stackedBarGroups = cache.svg.select(".chart-group") | ||
.selectAll(".mark-group") | ||
.data(cache.stack(cache.stackData)) | ||
const stackedBarGroups = cache.svg.selectAll(".bar-group") | ||
.data(data.stack(data.stackData)) | ||
const stackedUpdate = stackedBarGroups.enter() | ||
.append("g") | ||
.attr("class", "mark-group") | ||
.attr("class", "bar-group") | ||
.merge(stackedBarGroups) | ||
.attr("fill", (d) => cache.colorScale(d.key)) | ||
.attr("fill", (d) => scales.colorScale(d.key)) | ||
.attr("stroke", "white") | ||
@@ -49,6 +90,6 @@ | ||
.merge(stackedBars) | ||
.attr("x", (d) => cache.xScale(d.data[keys.DATA])) | ||
.attr("y", (d) => cache.yScale(d[1])) | ||
.attr("height", (d) => cache.yScale(d[0]) - cache.yScale(d[1])) | ||
.attr("width", cache.xScale.bandwidth()) | ||
.attr("x", (d) => scales.xScale(d.data[keys.DATA])) | ||
.attr("y", (d) => scales.yScale(d[1])) | ||
.attr("height", (d) => scales.yScale(d[0]) - scales.yScale(d[1])) | ||
.attr("width", scales.xScale.bandwidth()) | ||
@@ -58,6 +99,33 @@ stackedBars.exit().remove() | ||
function drawMarks () { | ||
buildSVG() | ||
if (config.chartType === "bar") { | ||
drawBars() | ||
} else if (config.chartType === "stackedBar") { | ||
drawStackedBars() | ||
} | ||
} | ||
function setConfig (_config) { | ||
config = override(config, _config) | ||
return this | ||
} | ||
function setScales (_scales) { | ||
scales = override(scales, _scales) | ||
return this | ||
} | ||
function setData (_data) { | ||
data = Object.assign({}, data, _data) | ||
return this | ||
} | ||
return { | ||
drawBars, | ||
drawStackedBars | ||
setConfig, | ||
setScales, | ||
setData, | ||
drawMarks | ||
} | ||
} |
@@ -17,2 +17,3 @@ import * as d3 from "./helpers/d3-service" | ||
height: 500, | ||
keyType: "time", | ||
rangeFormat: "%b %d, %Y" | ||
@@ -33,2 +34,6 @@ } | ||
let scales = { | ||
xScale: null | ||
} | ||
// events | ||
@@ -73,5 +78,17 @@ const dispatcher = d3.dispatch("rangeChange") | ||
const format = d3.utcFormat(config.rangeFormat) | ||
cache.inputMin.text(format(new Date(cache.rangeMin)) || "") | ||
cache.inputMax.text(format(new Date(cache.rangeMax)) || "") | ||
const domain = scales.xScale.domain() | ||
let rangeMin = cache.rangeMin === null ? domain[0] : cache.rangeMin | ||
let rangeMax = cache.rangeMax === null ? domain[1] : cache.rangeMax | ||
if (config.keyType === "time") { | ||
const format = d3.utcFormat(config.rangeFormat) | ||
rangeMin = format(new Date(rangeMin)) | ||
rangeMax = format(new Date(rangeMax)) | ||
} else { | ||
const format = d3.format(config.rangeFormat) | ||
rangeMin = format(rangeMin) | ||
rangeMax = format(rangeMax) | ||
} | ||
cache.inputMin.text(rangeMin) | ||
cache.inputMax.text(rangeMax) | ||
} | ||
@@ -93,2 +110,7 @@ | ||
function setRangeMax (_rangeMax) { | ||
cache.rangeMax = _rangeMax | ||
return this | ||
} | ||
function setVisibility (_shouldBeVisible) { | ||
@@ -100,7 +122,2 @@ cache.isEnabled = _shouldBeVisible | ||
function setRangeMax (_rangeMax) { | ||
cache.rangeMax = _rangeMax | ||
return this | ||
} | ||
function on (...args) { | ||
@@ -116,2 +133,7 @@ dispatcher.on(...args) | ||
function setScales (_scales) { | ||
scales = override(scales, _scales) | ||
return this | ||
} | ||
function destroy () { | ||
@@ -129,4 +151,5 @@ if (cache.root) { | ||
setRangeMax, | ||
setScales, | ||
setVisibility | ||
} | ||
} |
@@ -39,2 +39,4 @@ import * as d3 from "./helpers/d3-service" | ||
let brushExtent = null | ||
// events | ||
@@ -53,7 +55,2 @@ const dispatcher = d3.dispatch("brushStart", "brushMove", "brushEnd") | ||
function extractBrushDimension (_data) { | ||
const merged = d3.merge(_data.map((d) => d[keys.VALUES])) | ||
return sortData(merged, config.keyType) | ||
} | ||
function buildBrush () { | ||
@@ -71,2 +68,4 @@ cache.brush = cache.brush || d3.brushX() | ||
.attr("height", cache.chartHeight) | ||
moveBrush() | ||
} | ||
@@ -80,3 +79,30 @@ | ||
function validateExtent (_brushExtent) { | ||
return (Array.isArray(_brushExtent) | ||
&& _brushExtent[0] !== null | ||
&& typeof _brushExtent[0] !== "undefined" | ||
&& _brushExtent[1] !== null | ||
&& typeof _brushExtent[1] !== "undefined") | ||
} | ||
function clampBrush (_dataExtent, _brushExtent) { | ||
return [Math.max(_dataExtent[0], _brushExtent[0]), Math.min(_dataExtent[1], _brushExtent[1])] | ||
} | ||
function moveBrush () { | ||
if (brushExtent === null) { | ||
return this | ||
} | ||
const dataExtent = scales.xScale.domain() | ||
const extent = clampBrush(dataExtent, brushExtent) | ||
if (extent) { | ||
cache.svg.call(cache.brush.move, extent.map((d) => scales.xScale(d))) | ||
} | ||
return this | ||
} | ||
function handleBrushStart () { | ||
if (!d3.event.sourceEvent || !d3.event.selection) { | ||
return | ||
} | ||
dispatcher.call("brushStart", this, getDataExtent(), config) | ||
@@ -86,2 +112,5 @@ } | ||
function handleBrushMove () { | ||
if (!d3.event.sourceEvent || !d3.event.selection) { | ||
return | ||
} | ||
dispatcher.call("brushMove", this, getDataExtent(), config) | ||
@@ -91,3 +120,3 @@ } | ||
function handleBrushEnd () { | ||
// Only transition after input, ignore empty selections. | ||
// Skip programatic setting and empty selection | ||
if (!d3.event.sourceEvent || !d3.event.selection) { | ||
@@ -98,7 +127,2 @@ return | ||
const dataExtent = getDataExtent() | ||
d3.select(this) | ||
.transition() | ||
.call(d3.event.target.move, dataExtent.map(scales.xScale)) | ||
dispatcher.call("brushEnd", this, dataExtent, config) | ||
@@ -115,54 +139,7 @@ } | ||
if (data.dataBySeries) { | ||
cache.data = extractBrushDimension(cloneData(data.dataBySeries)) | ||
buildBrush() | ||
} | ||
return this | ||
} | ||
// function setBrushByPercentages (_a, _b) { | ||
// const x0 = _a * cache.chartWidth | ||
// const x1 = _b * cache.chartWidth | ||
// brush.move(chartBrush, [x0, x1]) | ||
// } | ||
// function setBrushByDates (_dateA, _dateB) { | ||
// const x0 = cache.xScale(new Date(_dateA)) | ||
// const x1 = cache.xScale(new Date(_dateB)) | ||
// cache.brush.move(cache.chartBrush, [x0, x1]) | ||
// } | ||
// function updateHandlers (_dateExtent) { | ||
// if (_dateExtent === null) { | ||
// cache.handle.attr("display", "none") | ||
// } else { | ||
// cache.handle | ||
// .attr("display", null) | ||
// .attr("transform", (d, i) => `translate(${_dateExtent[i]},${cache.chartHeight / 2})`) | ||
// } | ||
// } | ||
// API | ||
/** | ||
* Gets or Sets the dateRange for the selected part of the brush | ||
* @param {String[]} _x Desired dateRange for the graph | ||
* @return { dateRange | module} Current dateRange or Chart module to chain calls | ||
* @public | ||
*/ | ||
// function dateRange (_x) { | ||
// if (!arguments.length) { | ||
// return dateRange | ||
// } | ||
// dateRange = _x | ||
// if (Array.isArray(dateRange)) { | ||
// setBrushByDates(...dateRange) | ||
// } | ||
// return this | ||
// } | ||
function on (...args) { | ||
@@ -175,3 +152,2 @@ dispatcher.on(...args) | ||
cache.isEnabled = _isEnabled | ||
drawBrush() | ||
return this | ||
@@ -185,2 +161,11 @@ } | ||
function setBrushExtent (_brushExtent) { | ||
if (validateExtent(_brushExtent)) { | ||
brushExtent = _brushExtent | ||
} else { | ||
brushExtent = null | ||
} | ||
return this | ||
} | ||
function setScales (_scales) { | ||
@@ -196,3 +181,3 @@ scales = override(scales, _scales) | ||
function destroy (_data) { | ||
function destroy () { | ||
cache.svg.remove() | ||
@@ -207,2 +192,3 @@ return this | ||
setScales, | ||
setBrushExtent, | ||
drawBrush, | ||
@@ -209,0 +195,0 @@ setVisibility, |
@@ -5,6 +5,7 @@ import * as d3 from "./helpers/d3-service" | ||
import {keys} from "./helpers/constants" | ||
import {cloneData, invertScale, sortData, override, throttle, rebind} from "./helpers/common" | ||
import {cloneData, override, throttle, rebind} from "./helpers/common" | ||
import Scale from "./scale" | ||
import Line from "./line" | ||
import Bar from "./bar" | ||
import Axis from "./axis" | ||
@@ -19,2 +20,3 @@ import Tooltip from "./tooltip" | ||
import Label from "./label" | ||
import DataManager from "./data-manager" | ||
@@ -44,2 +46,5 @@ export default function Chart (_container) { | ||
defaultColor: "skyblue", | ||
xDomain: "auto", | ||
yDomain: "auto", | ||
y2Domain: "auto", | ||
@@ -86,6 +91,6 @@ // axis | ||
// domain | ||
xDomain: null, | ||
yDomain: null, | ||
y2Domain: null, | ||
domainEditorIsEnabled: true, | ||
xDomainEditorFormat: "%b %d, %Y", | ||
yDomainEditorFormat: ".2f", | ||
y2DomainEditorFormat: ".2f", | ||
@@ -95,2 +100,3 @@ // brush range | ||
brushRangeMax: null, | ||
rangeFormat: "%b %d, %Y", | ||
brushRangeIsEnabled: true, | ||
@@ -139,8 +145,5 @@ | ||
// accessors | ||
const getKey = (d) => d[keys.DATA] | ||
const getID = (d) => d[keys.ID] | ||
// events | ||
const dispatcher = d3.dispatch("mouseOverPanel", "mouseOutPanel", "mouseMovePanel") | ||
const dataManager = DataManager() | ||
@@ -163,4 +166,2 @@ function render () { | ||
console.log("cache.chartWidth", cache.chartWidth, w, config.width) | ||
if (!cache.svg) { | ||
@@ -196,2 +197,3 @@ const template = `<div class="mapd3 mapd3-container"> | ||
line: Line(cache.panel), | ||
bar: Bar(cache.panel), | ||
tooltip: Tooltip(cache.container), | ||
@@ -255,2 +257,8 @@ legend: Legend(cache.container), | ||
components.bar | ||
.setConfig(config) | ||
.setScales(scales) | ||
.setData(dataObject) | ||
.drawMarks() | ||
components.tooltip | ||
@@ -283,4 +291,5 @@ .setConfig(config) | ||
.setData(dataObject) | ||
.setBrushExtent([config.brushRangeMin, config.brushRangeMax]) | ||
.setVisibility(config.brushIsEnabled) | ||
.drawBrush() | ||
.setVisibility(config.brushIsEnabled) | ||
@@ -302,2 +311,3 @@ components.hover | ||
.setConfig(config) | ||
.setScales(scales) | ||
.setXDomain(config.xDomain) | ||
@@ -311,2 +321,3 @@ .setYDomain(config.yDomain) | ||
.setConfig(config) | ||
.setScales(scales) | ||
.setRangeMin(config.brushRangeMin) | ||
@@ -330,3 +341,3 @@ .setRangeMax(config.brushRangeMax) | ||
dataObject.data = cloneData(_data[keys.SERIES]) | ||
const cleanedData = cleanData(_data) | ||
const cleanedData = dataManager.cleanData(_data, config.keyType) | ||
Object.assign(dataObject, cleanedData) | ||
@@ -338,67 +349,2 @@ | ||
function cleanData (_data) { | ||
const dataBySeries = cloneData(_data[keys.SERIES]) | ||
const flatData = [] | ||
// Normalize dataBySeries | ||
dataBySeries.forEach((serie) => { | ||
serie[keys.VALUES] = sortData(serie[keys.VALUES], config.keyType) | ||
serie[keys.VALUES].forEach((d) => { | ||
d[keys.DATA] = config.keyType === "time" ? new Date(d[keys.DATA]) : d[keys.DATA] | ||
d[keys.VALUE] = Number(d[keys.VALUE]) | ||
}) | ||
}) | ||
dataBySeries.forEach((serie) => { | ||
serie[keys.VALUES].forEach((d) => { | ||
const dataPoint = {} | ||
dataPoint[keys.LABEL] = serie[keys.LABEL] | ||
dataPoint[keys.GROUP] = serie[keys.GROUP] | ||
dataPoint[keys.ID] = serie[keys.ID] | ||
dataPoint[keys.DATA] = config.keyType === "time" ? new Date(d[keys.DATA]) : d[keys.DATA] | ||
dataPoint[keys.VALUE] = d[keys.VALUE] | ||
flatData.push(dataPoint) | ||
}) | ||
}) | ||
const flatDataSorted = sortData(flatData, config.keyType) | ||
const dataByKey = d3.nest() | ||
.key(getKey) | ||
.entries(flatDataSorted) | ||
.map((d) => { | ||
const dataPoint = {} | ||
dataPoint[keys.DATA] = config.keyType === "time" ? new Date(d.key) : d.key | ||
dataPoint[keys.SERIES] = d.values | ||
return dataPoint | ||
}) | ||
const groupKeys = {} | ||
dataBySeries.forEach((d) => { | ||
if (!groupKeys[d[keys.GROUP]]) { | ||
groupKeys[d[keys.GROUP]] = [] | ||
} | ||
groupKeys[d[keys.GROUP]].push(d[keys.ID]) | ||
}) | ||
const stackData = dataByKey | ||
.map((d) => { | ||
const points = { | ||
key: d[keys.DATA] | ||
} | ||
d.series.forEach((dB) => { | ||
points[dB[keys.ID]] = dB[keys.VALUE] | ||
}) | ||
return points | ||
}) | ||
const stack = d3.stack() | ||
.keys(dataBySeries.map(getID)) | ||
.order(d3.stackOrderNone) | ||
.offset(d3.stackOffsetNone) | ||
return {dataBySeries, dataByKey, stack, stackData, flatDataSorted, groupKeys} | ||
} | ||
function triggerIntroAnimation () { | ||
@@ -421,22 +367,2 @@ if (config.isAnimated) { | ||
function getNearestDataPoint (_mouseX) { | ||
const keyFromInvertedX = invertScale(scales.xScale, _mouseX, config.keyType) | ||
const bisectLeft = d3.bisector(getKey).left | ||
const dataEntryIndex = bisectLeft(dataObject.dataByKey, keyFromInvertedX) | ||
const dataEntryForXPosition = dataObject.dataByKey[dataEntryIndex] | ||
const dataEntryForXPositionPrev = dataObject.dataByKey[Math.max(dataEntryIndex - 1, 0)] | ||
let nearestDataPoint = null | ||
if (keyFromInvertedX) { | ||
if ((keyFromInvertedX - dataEntryForXPositionPrev.key) | ||
< (dataEntryForXPosition.key - keyFromInvertedX)) { | ||
nearestDataPoint = dataEntryForXPositionPrev | ||
} else { | ||
nearestDataPoint = dataEntryForXPosition | ||
} | ||
} | ||
return nearestDataPoint | ||
} | ||
function addEvents () { | ||
@@ -459,3 +385,3 @@ const THROTTLE_DELAY = 20 | ||
const xPosition = mouseX | ||
const dataPoint = getNearestDataPoint(xPosition) | ||
const dataPoint = dataManager.getNearestDataPoint(xPosition, dataObject, scales, config.keyType) | ||
@@ -469,2 +395,9 @@ if (dataPoint) { | ||
function getEvents () { | ||
if (!cache.svg) { | ||
render() | ||
} | ||
return eventCollector | ||
} | ||
function on (...args) { | ||
@@ -490,4 +423,4 @@ dispatcher.on(...args) | ||
destroy, | ||
events: eventCollector | ||
getEvents | ||
} | ||
} |
import * as d3 from "./helpers/d3-service" | ||
import {keys} from "./helpers/constants" | ||
import {cloneData} from "./helpers/common" | ||
import {invertScale, sortData, cloneData} from "./helpers/common" | ||
export default function DataManager () { | ||
@@ -20,2 +21,8 @@ /* eslint-disable no-magic-numbers */ | ||
// accessors | ||
const getKey = (d) => d[keys.DATA] | ||
const getID = (d) => d[keys.ID] | ||
const DAY_IN_MS = 1000 * 60 * 60 * 24 | ||
function generateRandomString (_length) { | ||
@@ -46,3 +53,4 @@ return Math.random().toString(36).replace(/[^a-z0-9]+/g, "").substr(0, _length || 5) | ||
cache.baseDate = new Date() | ||
dataKeys = d3.timeDay.range(d3.timeMonth.floor(cache.baseDate), d3.timeMonth.ceil(cache.baseDate)) | ||
const previousDate = new Date(cache.baseDate.getTime() - DAY_IN_MS * config.pointCount) | ||
dataKeys = d3.timeDay.range(previousDate, cache.baseDate) | ||
} else if (config.keyType === "string") { | ||
@@ -69,2 +77,86 @@ dataKeys = d3.range(0, config.pointCount).map(() => generateRandomString()) | ||
function cleanData (_data, _keyType) { | ||
const dataBySeries = cloneData(_data[keys.SERIES]) | ||
const flatData = [] | ||
// Normalize dataBySeries | ||
dataBySeries.forEach((serie) => { | ||
serie[keys.VALUES] = sortData(serie[keys.VALUES], _keyType) | ||
serie[keys.VALUES].forEach((d) => { | ||
d[keys.DATA] = _keyType === "time" ? new Date(d[keys.DATA]) : d[keys.DATA] | ||
d[keys.VALUE] = Number(d[keys.VALUE]) | ||
}) | ||
}) | ||
dataBySeries.forEach((serie) => { | ||
serie[keys.VALUES].forEach((d) => { | ||
const dataPoint = {} | ||
dataPoint[keys.LABEL] = serie[keys.LABEL] | ||
dataPoint[keys.GROUP] = serie[keys.GROUP] | ||
dataPoint[keys.ID] = serie[keys.ID] | ||
dataPoint[keys.DATA] = _keyType === "time" ? new Date(d[keys.DATA]) : d[keys.DATA] | ||
dataPoint[keys.VALUE] = d[keys.VALUE] | ||
flatData.push(dataPoint) | ||
}) | ||
}) | ||
const flatDataSorted = sortData(flatData, _keyType) | ||
const dataByKey = d3.nest() | ||
.key(getKey) | ||
.entries(flatDataSorted) | ||
.map((d) => { | ||
const dataPoint = {} | ||
dataPoint[keys.DATA] = _keyType === "time" ? new Date(d.key) : d.key | ||
dataPoint[keys.SERIES] = d.values | ||
return dataPoint | ||
}) | ||
const groupKeys = {} | ||
dataBySeries.forEach((d) => { | ||
if (!groupKeys[d[keys.GROUP]]) { | ||
groupKeys[d[keys.GROUP]] = [] | ||
} | ||
groupKeys[d[keys.GROUP]].push(d[keys.ID]) | ||
}) | ||
const stackData = dataByKey | ||
.map((d) => { | ||
const points = { | ||
key: d[keys.DATA] | ||
} | ||
d.series.forEach((dB) => { | ||
points[dB[keys.ID]] = dB[keys.VALUE] | ||
}) | ||
return points | ||
}) | ||
const stack = d3.stack() | ||
.keys(dataBySeries.map(getID)) | ||
.order(d3.stackOrderNone) | ||
.offset(d3.stackOffsetNone) | ||
return {dataBySeries, dataByKey, stack, stackData, flatDataSorted, groupKeys} | ||
} | ||
function getNearestDataPoint (_mouseX, _dataObject, _scales, _keyType) { | ||
const keyFromInvertedX = invertScale(_scales.xScale, _mouseX, _keyType) | ||
const bisectLeft = d3.bisector(getKey).left | ||
const dataEntryIndex = bisectLeft(_dataObject.dataByKey, keyFromInvertedX) | ||
const dataEntryForXPosition = _dataObject.dataByKey[dataEntryIndex] | ||
const dataEntryForXPositionPrev = _dataObject.dataByKey[Math.max(dataEntryIndex - 1, 0)] | ||
let nearestDataPoint = null | ||
if (keyFromInvertedX && dataEntryForXPosition && dataEntryForXPositionPrev) { | ||
if ((keyFromInvertedX - dataEntryForXPositionPrev.key) | ||
< (dataEntryForXPosition.key - keyFromInvertedX)) { | ||
nearestDataPoint = dataEntryForXPositionPrev | ||
} else { | ||
nearestDataPoint = dataEntryForXPosition | ||
} | ||
} | ||
return nearestDataPoint | ||
} | ||
function filterByKey (_extent) { | ||
@@ -106,2 +198,4 @@ const data = cloneData(cache.data) | ||
generateSeries, | ||
cleanData, | ||
getNearestDataPoint, | ||
filterByDate, | ||
@@ -108,0 +202,0 @@ filterByKey, |
@@ -16,3 +16,7 @@ import * as d3 from "./helpers/d3-service" | ||
width: 800, | ||
height: 500 | ||
height: 500, | ||
keyType: null, | ||
xDomainEditorFormat: "%b %d, %Y", | ||
yDomainEditorFormat: ".2f", | ||
y2DomainEditorFormat: ".2f" | ||
} | ||
@@ -37,8 +41,12 @@ | ||
chartHeight: null, | ||
xDomain: null, | ||
yDomain: null, | ||
y2Domain: null, | ||
isEnabled: true | ||
} | ||
let scales = { | ||
xScale: null, | ||
yScale: null, | ||
yScale2: null, | ||
hasSecondAxis: null | ||
} | ||
// events | ||
@@ -50,3 +58,18 @@ const dispatcher = d3.dispatch("domainChange", "domainLockToggle") | ||
cache.chartHeight = config.height - config.margin.top - config.margin.bottom | ||
const xDomain = cache.xDomain === "auto" ? scales.xScale.domain() : cache.xDomain | ||
const yDomain = cache.yDomain === "auto" ? scales.yScale.domain() : cache.yDomain | ||
const y2Domain = (cache.y2Domain === "auto" && scales.y2Scale) ? scales.y2Scale.domain() : cache.y2Domain | ||
let xFormatter = (d) => d | ||
if (config.xDomainEditorFormat) { | ||
if (config.keyType === "time") { | ||
xFormatter = d3.timeFormat(config.xDomainEditorFormat) | ||
} | ||
} else if (config.keyType === "number") { | ||
xFormatter = d3.format(config.xDomainEditorFormat) | ||
} | ||
const yFormatter = d3.format(config.yDomainEditorFormat) | ||
const y2Formatter = d3.format(config.y2DomainEditorFormat) | ||
if (!cache.root) { | ||
@@ -78,4 +101,7 @@ cache.root = cache.container | ||
.style("position", "absolute") | ||
if (scales.hasSecondAxis) { | ||
cache.y2HitZone | ||
.on("mouseover.dispatch", showY2Editor) | ||
.on("mouseout.dispatch", hideY2Editor) | ||
} | ||
@@ -198,4 +224,3 @@ // y input group | ||
.style("top", `${HOVER_ZONE_SIZE}px`) | ||
.text(Array.isArray(cache.yDomain) | ||
&& !isNaN(cache.yDomain[1]) ? cache.yDomain[1] : "") | ||
.text(yFormatter(yDomain[1])) | ||
@@ -205,4 +230,3 @@ cache.yMinInput | ||
.style("top", `${cache.chartHeight + HOVER_ZONE_SIZE - INPUT_HEIGHT}px`) | ||
.text(Array.isArray(cache.yDomain) | ||
&& !isNaN(cache.yDomain[0]) ? cache.yDomain[0] : "") | ||
.text(yFormatter(yDomain[0])) | ||
@@ -219,4 +243,3 @@ cache.yLockIcon | ||
.style("left", `${PADDING}px`) | ||
.text(Array.isArray(cache.y2Domain) | ||
&& !isNaN(cache.y2Domain[1]) ? cache.y2Domain[1] : "") | ||
.text(y2Formatter(y2Domain[1])) | ||
@@ -227,4 +250,3 @@ cache.y2MinInput | ||
.style("left", `${PADDING}px`) | ||
.text(Array.isArray(cache.y2Domain) | ||
&& !isNaN(cache.y2Domain[0]) ? cache.y2Domain[0] : "") | ||
.text(y2Formatter(y2Domain[0])) | ||
@@ -240,4 +262,3 @@ cache.y2LockIcon | ||
.style("left", `${HOVER_ZONE_SIZE}px`) | ||
.text(Array.isArray(cache.xDomain) | ||
&& typeof (cache.xDomain[0]) !== "undefined" ? cache.xDomain[0] : "") | ||
.text(xFormatter(xDomain[0])) | ||
@@ -248,4 +269,3 @@ cache.xMaxInput | ||
.style("left", `${HOVER_ZONE_SIZE + cache.chartWidth - INPUT_WIDTH}px`) | ||
.text(Array.isArray(cache.xDomain) | ||
&& typeof (cache.xDomain[1]) !== "undefined" ? cache.xDomain[1] : "") | ||
.text(xFormatter(xDomain[1])) | ||
@@ -332,2 +352,7 @@ cache.xLockIcon | ||
function setScales (_scales) { | ||
scales = override(scales, _scales) | ||
return this | ||
} | ||
function setConfig (_config) { | ||
@@ -346,2 +371,3 @@ config = override(config, _config) | ||
on, | ||
setScales, | ||
setConfig, | ||
@@ -348,0 +374,0 @@ setXDomain, |
import Tooltip from "./tooltip" | ||
export default function Legend (_container) { | ||
return Tooltip(_container) | ||
const IS_LEGEND = true | ||
return Tooltip(_container, IS_LEGEND) | ||
} |
@@ -68,3 +68,4 @@ import * as d3 from "./helpers/d3-service" | ||
.merge(lines) | ||
.attr("class", () => ["mark", "line"].join(" ")) | ||
.attr("class", "mark line") | ||
.classed("y2-line", (d) => d[keys.GROUP] > 0) | ||
.attr("d", (d) => { | ||
@@ -87,3 +88,3 @@ if (d[keys.GROUP] === 0) { | ||
.y0((d) => scales.yScale(d[keys.VALUE])) | ||
.y1(() => config.chartHeight) | ||
.y1(() => cache.chartHeight) | ||
@@ -93,3 +94,3 @@ const seriesArea2 = d3.area() | ||
.y0((d) => scales.yScale2(d[keys.VALUE])) | ||
.y1(() => config.chartHeight) | ||
.y1(() => cache.chartHeight) | ||
.curve(d3.curveCatmullRom) | ||
@@ -103,3 +104,4 @@ | ||
.merge(areas) | ||
.attr("class", () => ["mark", "area"].join(" ")) | ||
.attr("class", "mark area") | ||
.classed("y2-area", (d) => d[keys.GROUP] > 0) | ||
.attr("d", (d) => { | ||
@@ -130,3 +132,3 @@ if (d[keys.GROUP] === 0) { | ||
.merge(areas) | ||
.attr("class", () => ["mark", "stacked-area"].join(" ")) | ||
.attr("class", "mark stacked-area") | ||
.attr("d", seriesLine) | ||
@@ -133,0 +135,0 @@ .style("stroke", "none") |
@@ -20,3 +20,6 @@ import * as d3 from "./helpers/d3-service" | ||
colorSchema: null, | ||
defaultColor: null | ||
defaultColor: null, | ||
xDomain: "auto", | ||
yDomain: "auto", | ||
y2Domain: "auto" | ||
} | ||
@@ -37,12 +40,10 @@ | ||
const chartWidth = config.width - config.margin.left - config.margin.right | ||
let xScale = null | ||
let domain = null | ||
let xScale = null | ||
if (config.keyType === "time") { | ||
domain = d3.extent(_allKeys) | ||
xScale = d3.scaleTime() | ||
} else if (config.keyType === "number") { | ||
domain = d3.extent(_allKeys) | ||
xScale = d3.scaleLinear() | ||
} else { | ||
domain = _allKeys | ||
xScale = (config.chartType === "bar" || config.chartType === "stackedBar") ? d3.scaleBand() : d3.scalePoint() | ||
@@ -52,2 +53,12 @@ xScale.padding(0) | ||
if (config.xDomain === "auto") { | ||
if (config.keyType === "string") { | ||
domain = _allKeys | ||
} else { | ||
domain = d3.extent(_allKeys) | ||
} | ||
} else { | ||
domain = config.xDomain | ||
} | ||
xScale.domain(domain) | ||
@@ -99,4 +110,2 @@ .range([0, chartWidth]) | ||
const valuesExtent = d3.extent(allStackHeights) | ||
const allKeys = data.flatDataSorted.map(getKey) | ||
@@ -107,4 +116,12 @@ const allUniqueKeys = getUnique(allKeys) | ||
const colorScale = buildColorScale() | ||
const yScale = buildYScale([0, valuesExtent[1]]) | ||
let yDomain = null | ||
if (config.yDomain === "auto") { | ||
const valuesExtent = d3.extent(allStackHeights) | ||
yDomain = [0, valuesExtent[1]] | ||
} else { | ||
yDomain = config.yDomain | ||
} | ||
const yScale = buildYScale(yDomain) | ||
return { | ||
@@ -124,15 +141,26 @@ xScale, | ||
const allUniqueKeys = groupAxis1.allKeys | ||
const valuesExtent = d3.extent(groupAxis1.allValues) | ||
const xScale = buildXScale(allUniqueKeys) | ||
const colorScale = buildColorScale() | ||
const yScale = buildYScale(valuesExtent) | ||
let yDomain = null | ||
if (config.yDomain === "auto") { | ||
yDomain = d3.extent(groupAxis1.allValues) | ||
} else { | ||
yDomain = config.yDomain | ||
} | ||
const yScale = buildYScale(yDomain) | ||
let yScale2 = null | ||
if (hasSecondAxis) { | ||
const groupAxis2 = groups[1] | ||
const valuesExtent2 = d3.extent(groupAxis2.allValues) | ||
let y2Domain = null | ||
if (config.y2Domain === "auto") { | ||
const groupAxis2 = groups[1] | ||
y2Domain = d3.extent(groupAxis2.allValues) | ||
} else { | ||
y2Domain = config.y2Domain | ||
} | ||
yScale2 = yScale.copy() | ||
.domain(valuesExtent2) | ||
.domain(y2Domain) | ||
} | ||
@@ -139,0 +167,0 @@ |
@@ -6,3 +6,3 @@ import * as d3 from "./helpers/d3-service" | ||
export default function Tooltip (_container) { | ||
export default function Tooltip (_container, isLegend = false) { | ||
@@ -61,3 +61,3 @@ let config = { | ||
cache.root = cache.container.append("div") | ||
.attr("class", "tooltip-group") | ||
.attr("class", isLegend ? "legend-group" : "tooltip-group") | ||
.style("position", "absolute") | ||
@@ -121,4 +121,4 @@ .style("pointer-events", "none") | ||
const legendData = [ | ||
{key: "color", value: scales.colorScale(d[keys.ID])}, | ||
{key: "label", value: d[keys.LABEL]} | ||
{key: "tooltip-color", value: scales.colorScale(d[keys.ID])}, | ||
{key: "tooltip-label", value: d[keys.LABEL]} | ||
] | ||
@@ -135,3 +135,3 @@ if (typeof d[keys.VALUE] !== "undefined") { | ||
const selection = d3.select(this) | ||
if (d.key === "color") { | ||
if (d.key === "tooltip-color") { | ||
selection.style("background", d.value) | ||
@@ -138,0 +138,0 @@ } else if (d.key === "value") { |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is not supported yet
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
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
2887574
6783