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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions src/Cartesian/Axis.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import React from "react"
import PropTypes from "prop-types"
import * as d3 from 'd3'
import { format } from 'd3-format'
import { useDimensionsContext } from "../Components/Chart";

const axisComponentsByDimension = {
Expand Down Expand Up @@ -31,7 +31,7 @@ Axis.propTypes = {
Axis.defaultProps = {
dimension: "x",
scale: null,
formatTick: d3.format(","),
formatTick: format(","),
}

export default Axis
Expand Down
12 changes: 7 additions & 5 deletions src/Charts/BarChart.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import React, { useCallback, useMemo } from "react"
import PropTypes from "prop-types"
import * as d3 from "d3"
import { scaleBand, scaleLinear } from 'd3-scale'
import { max } from 'd3-array'
import { format } from 'd3-format'

import Chart from "../Components/Chart"
import Bars from "../Components/Bars"
Expand All @@ -11,7 +13,7 @@ import { useChartDimensions, accessorPropsType } from "../Utils/utils"
import { useTooltip } from "../Utils/useTooltip"

const DEFAULT_COLOR = '#9980FA'
const fmt = d3.format(",")
const fmt = format(",")

const BarChart = ({
data, xAccessor, yAccessor, xLabel, yLabel,
Expand All @@ -22,16 +24,16 @@ const BarChart = ({
const { wrapperRef, tooltip, showTooltip, moveTooltip, hideTooltip } = useTooltip()

const xScale = useMemo(() =>
d3.scaleBand()
scaleBand()
.domain(data ? data.map(xAccessor) : [])
.range([0, dimensions.boundedWidth])
.padding(barPadding ?? 0.2),
[data, xAccessor, dimensions.boundedWidth, barPadding]
)

const yScale = useMemo(() =>
d3.scaleLinear()
.domain([yMin ?? 0, d3.max(data, yAccessor) || 0])
scaleLinear()
.domain([yMin ?? 0, max(data, yAccessor) || 0])
.range([dimensions.boundedHeight, 0])
.nice(),
[data, yAccessor, dimensions.boundedHeight, yMin]
Expand Down
18 changes: 10 additions & 8 deletions src/Charts/Histogram.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import React, { useCallback, useMemo } from "react"
import PropTypes from "prop-types"
import * as d3 from "d3"
import { scaleLinear } from 'd3-scale'
import { extent, max, histogram } from 'd3-array'
import { format } from 'd3-format'

import Chart from "../Components/Chart"
import Bars from "../Components/Bars"
Expand All @@ -13,8 +15,8 @@ import { useTooltip } from "../Utils/useTooltip"

const DEFAULT_GRADIENT = ["#9980FA", "rgb(226, 222, 243)"]
const DEFAULT_COLOR = '#9980FA'
const fmt = d3.format(",")
const fmtFixed = d3.format(",.2~f")
const fmt = format(",")
const fmtFixed = format(",.2~f")
const BAR_PADDING = 2

const Histogram = ({
Expand All @@ -30,24 +32,24 @@ const Histogram = ({
const numberOfThresholds = thresholds || 9

const xScale = useMemo(() =>
d3.scaleLinear()
.domain(d3.extent(data, xAccessor))
scaleLinear()
.domain(extent(data, xAccessor))
.range([0, dimensions.boundedWidth])
.nice(numberOfThresholds),
[data, xAccessor, dimensions.boundedWidth, numberOfThresholds]
)

const bins = useMemo(() =>
d3.histogram()
histogram()
.domain(xScale.domain())
.value(xAccessor)
.thresholds(xScale.ticks(numberOfThresholds))(data),
[xScale, xAccessor, numberOfThresholds, data]
)

const yScale = useMemo(() =>
d3.scaleLinear()
.domain([0, d3.max(bins, d => d.length) || 0])
scaleLinear()
.domain([0, max(bins, d => d.length) || 0])
.range([dimensions.boundedHeight, 0])
.nice(),
[bins, dimensions.boundedHeight]
Expand Down
26 changes: 15 additions & 11 deletions src/Charts/PieChart.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
import React, { useCallback, useMemo } from "react"
import PropTypes from "prop-types"
import * as d3 from "d3"
import { scaleOrdinal } from 'd3-scale'
import { arc, pie } from 'd3-shape'
import { sum } from 'd3-array'
import { schemeSet2 } from 'd3-scale-chromatic'
import { format } from 'd3-format'

import Chart from "../Components/Chart"
import ChartLayout from "../Components/ChartLayout"
Expand All @@ -10,7 +14,7 @@ import { useTooltip } from "../Utils/useTooltip"

const DEFAULT_MARGIN = { marginTop: 20, marginRight: 20, marginBottom: 20, marginLeft: 20 }
const MIN_LABEL_ANGLE = 0.35
const fmt = d3.format(",")
const fmt = format(",")

const PieChart = ({
data, valueAccessor, labelAccessor,
Expand All @@ -21,7 +25,7 @@ const PieChart = ({
const { wrapperRef, tooltip, showTooltip, moveTooltip, hideTooltip } = useTooltip()

const colorScale = useMemo(() =>
d3.scaleOrdinal(Array.isArray(colors) ? colors : d3.schemeSet2),
scaleOrdinal(Array.isArray(colors) ? colors : schemeSet2),
[colors]
)

Expand All @@ -34,19 +38,19 @@ const PieChart = ({
: 0

const arcGenerator = useMemo(() =>
d3.arc().innerRadius(resolvedInnerRadius).outerRadius(outerRadius),
arc().innerRadius(resolvedInnerRadius).outerRadius(outerRadius),
[resolvedInnerRadius, outerRadius]
)

const labelArc = useMemo(() => {
const r = resolvedInnerRadius > 0
? (resolvedInnerRadius + outerRadius) / 2
: outerRadius * 0.65
return d3.arc().innerRadius(r).outerRadius(r)
return arc().innerRadius(r).outerRadius(r)
}, [resolvedInnerRadius, outerRadius])

const arcs = useMemo(() =>
data ? d3.pie()
data ? pie()
.value(valueAccessor)
.padAngle(padAngle ?? 0.02)
.sort(null)(data)
Expand All @@ -55,7 +59,7 @@ const PieChart = ({
)

const total = useMemo(() =>
data ? d3.sum(data, valueAccessor) : 0,
data ? sum(data, valueAccessor) : 0,
[data, valueAccessor]
)

Expand Down Expand Up @@ -95,22 +99,22 @@ const PieChart = ({
<div className="PieChart" ref={ref} style={{ flex: 1, minHeight: 0, minWidth: 0 }}>
<Chart dimensions={dimensions} label="Pie chart">
<g transform={`translate(${cx}, ${cy})`}>
{arcs.map((arc, i) => {
{arcs.map((a, i) => {
const label = labelAccessor ? labelAccessor(data[i]) : String(i)
return (
<g key={i} className="PieChart__slice-group">
<path
className="PieChart__slice"
d={arcGenerator(arc)}
d={arcGenerator(a)}
style={{ fill: colorScale(label) }}
onMouseEnter={e => handleSliceEnter(data[i], label, e)}
onMouseMove={e => handleSliceMove(data[i], label, e)}
onMouseLeave={hideTooltip}
/>
{displayLabels && (arc.endAngle - arc.startAngle) >= MIN_LABEL_ANGLE && (
{displayLabels && (a.endAngle - a.startAngle) >= MIN_LABEL_ANGLE && (
<text
className="PieChart__label"
transform={`translate(${labelArc.centroid(arc)})`}
transform={`translate(${labelArc.centroid(a)})`}
style={{ textAnchor: 'middle', dominantBaseline: 'middle', pointerEvents: 'none' }}
>
{label}
Expand Down
18 changes: 10 additions & 8 deletions src/Charts/ScatterPlot.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import React, { useCallback, useMemo } from "react"
import PropTypes from "prop-types"
import * as d3 from "d3"
import { scaleLinear } from 'd3-scale'
import { extent } from 'd3-array'
import { format } from 'd3-format'

import Chart from "../Components/Chart"
import Circles from "../Components/Circles"
Expand All @@ -11,7 +13,7 @@ import { useChartDimensions, accessorPropsType } from "../Utils/utils"
import { useTooltip } from "../Utils/useTooltip"

const DEFAULT_COLOR = '#9980FA'
const fmt = d3.format(",")
const fmt = format(",")

const ScatterPlot = ({
data, xAccessor, yAccessor, xLabel, yLabel,
Expand All @@ -22,15 +24,15 @@ const ScatterPlot = ({
const { wrapperRef, tooltip, showTooltip, moveTooltip, hideTooltip } = useTooltip()

const xScale = useMemo(() => {
const extent = d3.extent(data, xAccessor)
const domain = extent[0] === extent[1] ? [extent[0] - 1, extent[0] + 1] : extent
return d3.scaleLinear().domain(domain).range([0, dimensions.boundedWidth]).nice()
const ext = extent(data, xAccessor)
const domain = ext[0] === ext[1] ? [ext[0] - 1, ext[0] + 1] : ext
return scaleLinear().domain(domain).range([0, dimensions.boundedWidth]).nice()
}, [data, xAccessor, dimensions.boundedWidth])

const yScale = useMemo(() => {
const extent = d3.extent(data, yAccessor)
const domain = extent[0] === extent[1] ? [extent[0] - 1, extent[0] + 1] : extent
return d3.scaleLinear().domain(domain).range([dimensions.boundedHeight, 0]).nice()
const ext = extent(data, yAccessor)
const domain = ext[0] === ext[1] ? [ext[0] - 1, ext[0] + 1] : ext
return scaleLinear().domain(domain).range([dimensions.boundedHeight, 0]).nice()
}, [data, yAccessor, dimensions.boundedHeight])

const legendItems = useMemo(() =>
Expand Down
18 changes: 11 additions & 7 deletions src/Charts/StackedBarChart.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
import React, { useCallback, useMemo } from "react"
import PropTypes from "prop-types"
import * as d3 from "d3"
import { scaleOrdinal, scaleBand, scaleLinear } from 'd3-scale'
import { stack } from 'd3-shape'
import { max } from 'd3-array'
import { schemeSet2 } from 'd3-scale-chromatic'
import { format } from 'd3-format'

import Chart from "../Components/Chart"
import Axis from "../Cartesian/Axis"
Expand All @@ -9,7 +13,7 @@ import Tooltip from "../Components/Tooltip"
import { useChartDimensions, accessorPropsType } from "../Utils/utils"
import { useTooltip } from "../Utils/useTooltip"

const fmt = d3.format(",")
const fmt = format(",")

const StackedBarChart = ({
data, xAccessor, keys, colors,
Expand All @@ -20,26 +24,26 @@ const StackedBarChart = ({
const { wrapperRef, tooltip, showTooltip, moveTooltip, hideTooltip } = useTooltip()

const colorScale = useMemo(() =>
d3.scaleOrdinal(Array.isArray(colors) ? colors : d3.schemeSet2).domain(keys),
scaleOrdinal(Array.isArray(colors) ? colors : schemeSet2).domain(keys),
[colors, keys]
)

const series = useMemo(() =>
data && keys && keys.length ? d3.stack().keys(keys)(data) : [],
data && keys && keys.length ? stack().keys(keys)(data) : [],
[data, keys]
)

const xScale = useMemo(() =>
d3.scaleBand()
scaleBand()
.domain(data ? data.map(xAccessor) : [])
.range([0, dimensions.boundedWidth])
.padding(barPadding ?? 0.2),
[data, xAccessor, dimensions.boundedWidth, barPadding]
)

const yScale = useMemo(() =>
d3.scaleLinear()
.domain([0, d3.max(series, s => d3.max(s, d => d[1])) || 0])
scaleLinear()
.domain([0, max(series, s => max(s, d => d[1])) || 0])
.range([dimensions.boundedHeight, 0])
.nice(),
[series, dimensions.boundedHeight]
Expand Down
25 changes: 14 additions & 11 deletions src/Charts/Timeline.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
import React, { useCallback, useMemo, useState } from "react"
import PropTypes from "prop-types"
import * as d3 from "d3"
import { scaleTime, scaleLinear } from 'd3-scale'
import { extent, bisector } from 'd3-array'
import { timeFormat } from 'd3-time-format'
import { format } from 'd3-format'

import Chart from "../Components/Chart"
import Line from "../Components/Line"
Expand All @@ -12,11 +15,11 @@ import Tooltip from "../Components/Tooltip"
import { useChartDimensions, accessorPropsType, useUniqueId } from "../Utils/utils"
import { useTooltip } from "../Utils/useTooltip"

const formatDate = d3.timeFormat("%-b %-d")
const formatTooltipDate = d3.timeFormat("%-b %-d, %Y")
const formatDate = timeFormat("%-b %-d")
const formatTooltipDate = timeFormat("%-b %-d, %Y")
const DEFAULT_GRADIENT = ["rgb(226, 222, 243)", "#f8f9fa"]
const DEFAULT_COLOR = '#9980FA'
const fmt = d3.format(",")
const fmt = format(",")

const Timeline = ({
data, xAccessor, yAccessor, xLabel, yLabel,
Expand All @@ -34,15 +37,15 @@ const Timeline = ({
|| (color ? [`${color}55`, "rgba(255,255,255,0)"] : DEFAULT_GRADIENT)

const xScale = useMemo(() =>
d3.scaleTime()
.domain(d3.extent(data, xAccessor))
scaleTime()
.domain(extent(data, xAccessor))
.range([0, dimensions.boundedWidth]),
[data, xAccessor, dimensions.boundedWidth]
)

const yScale = useMemo(() =>
d3.scaleLinear()
.domain(d3.extent(data, yAccessor))
scaleLinear()
.domain(extent(data, yAccessor))
.range([dimensions.boundedHeight, 0])
.nice(),
[data, yAccessor, dimensions.boundedHeight]
Expand All @@ -53,20 +56,20 @@ const Timeline = ({
[yLabel, color]
)

const bisect = useMemo(() => d3.bisector(xAccessor).left, [xAccessor])
const dateBisector = useMemo(() => bisector(xAccessor).left, [xAccessor])

const handleMouseMove = useCallback(e => {
const svgEl = e.currentTarget.ownerSVGElement
const { left: svgLeft } = svgEl.getBoundingClientRect()
const mouseX = e.clientX - svgLeft - dimensions.marginLeft
const date = xScale.invert(mouseX)
const idx = bisect(data, date, 1)
const idx = dateBisector(data, date, 1)
const d0 = data[idx - 1]
const d1 = data[idx]
const i = !d1 || (d0 && date - xAccessor(d0) < xAccessor(d1) - date) ? idx - 1 : idx
setHoveredIndex(i)
showTooltip(e, { datum: data[i] })
}, [data, xAccessor, xScale, bisect, dimensions.marginLeft, showTooltip])
}, [data, xAccessor, xScale, dateBisector, dimensions.marginLeft, showTooltip])

const handleMouseLeave = useCallback(() => {
setHoveredIndex(null)
Expand Down
6 changes: 3 additions & 3 deletions src/Components/Bars.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import React from "react"
import PropTypes from "prop-types"
import * as d3 from 'd3'
import { max } from 'd3-array'
import { accessorPropsType, callAccessor } from "../Utils/utils";

const Bars = ({ data, keyAccessor, xAccessor, yAccessor, widthAccessor, heightAccessor, onMouseEnter, onMouseLeave, onMouseMove, ...props }) => (
Expand All @@ -11,8 +11,8 @@ const Bars = ({ data, keyAccessor, xAccessor, yAccessor, widthAccessor, heightAc
key={keyAccessor(d, i)}
x={callAccessor(xAccessor, d, i)}
y={callAccessor(yAccessor, d, i)}
width={d3.max([callAccessor(widthAccessor, d, i), 0])}
height={d3.max([callAccessor(heightAccessor, d, i), 0])}
width={max([callAccessor(widthAccessor, d, i), 0])}
height={max([callAccessor(heightAccessor, d, i), 0])}
onMouseEnter={onMouseEnter ? e => onMouseEnter(d, i, e) : undefined}
onMouseLeave={onMouseLeave ? e => onMouseLeave(d, i, e) : undefined}
onMouseMove={onMouseMove ? e => onMouseMove(d, i, e) : undefined}
Expand Down
Loading
Loading