diff --git a/packages/line/index.d.ts b/packages/line/__old_index.d.ts
similarity index 96%
rename from packages/line/index.d.ts
rename to packages/line/__old_index.d.ts
index 694775dd70..9b0c465890 100644
--- a/packages/line/index.d.ts
+++ b/packages/line/__old_index.d.ts
@@ -1,11 +1,3 @@
-/*
- * This file is part of the nivo project.
- *
- * Copyright 2016-present, Raphaël Benitte.
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
import * as React from 'react'
import {
Dimensions,
diff --git a/packages/line/package.json b/packages/line/package.json
index e184091fa2..816942bc4d 100644
--- a/packages/line/package.json
+++ b/packages/line/package.json
@@ -21,11 +21,12 @@
],
"main": "./dist/nivo-line.cjs.js",
"module": "./dist/nivo-line.es.js",
+ "typings": "./dist/types/index.d.ts",
"files": [
"README.md",
"LICENSE.md",
- "index.d.ts",
- "dist/"
+ "dist/",
+ "!dist/tsconfig.tsbuildinfo"
],
"dependencies": {
"@nivo/annotations": "0.79.1",
diff --git a/packages/line/src/Areas.js b/packages/line/src/Areas.js
deleted file mode 100644
index c459aa4b14..0000000000
--- a/packages/line/src/Areas.js
+++ /dev/null
@@ -1,68 +0,0 @@
-/*
- * This file is part of the nivo project.
- *
- * Copyright 2016-present, Raphaël Benitte.
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-import { memo } from 'react'
-import PropTypes from 'prop-types'
-import { useSpring, animated } from '@react-spring/web'
-import { useAnimatedPath, useMotionConfig, blendModePropType } from '@nivo/core'
-
-const AreaPath = ({ areaBlendMode, areaOpacity, color, fill, path }) => {
- const { animate, config: springConfig } = useMotionConfig()
-
- const animatedPath = useAnimatedPath(path)
- const animatedProps = useSpring({
- color,
- config: springConfig,
- immediate: !animate,
- })
-
- return (
-
- )
-}
-
-AreaPath.propTypes = {
- areaBlendMode: blendModePropType.isRequired,
- areaOpacity: PropTypes.number.isRequired,
- color: PropTypes.string,
- fill: PropTypes.string,
- path: PropTypes.string.isRequired,
-}
-
-const Areas = ({ areaGenerator, areaOpacity, areaBlendMode, lines }) => {
- const computedLines = lines.slice(0).reverse()
-
- return (
-
- {computedLines.map(line => (
- d.position))}
- {...{ areaOpacity, areaBlendMode, ...line }}
- />
- ))}
-
- )
-}
-
-Areas.propTypes = {
- areaGenerator: PropTypes.func.isRequired,
- areaOpacity: PropTypes.number.isRequired,
- areaBlendMode: blendModePropType.isRequired,
- lines: PropTypes.arrayOf(PropTypes.object).isRequired,
-}
-
-export default memo(Areas)
diff --git a/packages/line/src/Areas.tsx b/packages/line/src/Areas.tsx
new file mode 100644
index 0000000000..40ffab76e9
--- /dev/null
+++ b/packages/line/src/Areas.tsx
@@ -0,0 +1,69 @@
+import { memo } from 'react'
+import { useSpring, animated } from '@react-spring/web'
+import { useAnimatedPath, useMotionConfig, CssMixBlendMode } from '@nivo/core'
+import { AreaGenerator, LineDatum } from './types'
+
+const AreaPath = ({
+ blendMode,
+ opacity,
+ color,
+ fill,
+ path,
+}: {
+ blendMode: CssMixBlendMode
+ opacity: number
+ color: string
+ fill?: string
+ path: string
+}) => {
+ const { animate, config: springConfig } = useMotionConfig()
+
+ const animatedPath = useAnimatedPath(path)
+ const animatedProps = useSpring({
+ color,
+ config: springConfig,
+ immediate: !animate,
+ })
+
+ return (
+
+ )
+}
+
+const NonMemoizedAreas = ({
+ areaGenerator,
+ areaOpacity,
+ areaBlendMode,
+ lines,
+}: {
+ areaGenerator: AreaGenerator
+ areaOpacity: number
+ areaBlendMode: CssMixBlendMode
+ lines: any[]
+}) => {
+ const computedLines = lines.slice(0).reverse()
+
+ return (
+
+ {computedLines.map(line => (
+ d.position))}
+ opacity={areaOpacity}
+ blendMode={areaBlendMode}
+ {...line}
+ />
+ ))}
+
+ )
+}
+
+export const Areas = memo(NonMemoizedAreas) as typeof NonMemoizedAreas
diff --git a/packages/line/src/Line.js b/packages/line/src/Line.tsx
similarity index 52%
rename from packages/line/src/Line.js
rename to packages/line/src/Line.tsx
index 7956479100..4299aeef26 100644
--- a/packages/line/src/Line.js
+++ b/packages/line/src/Line.tsx
@@ -1,17 +1,10 @@
-/*
- * This file is part of the nivo project.
- *
- * Copyright 2016-present, Raphaël Benitte.
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-import { Fragment, useState } from 'react'
+import { Fragment, ReactNode, useState } from 'react'
import {
- bindDefs,
- withContainer,
+ Container,
useDimensions,
useTheme,
+ // @ts-ignore
+ bindDefs,
SvgWrapper,
CartesianMarkers,
} from '@nivo/core'
@@ -19,82 +12,88 @@ import { useInheritedColor } from '@nivo/colors'
import { Axes, Grid } from '@nivo/axes'
import { BoxLegendSvg } from '@nivo/legends'
import { Crosshair } from '@nivo/tooltip'
+import { DefaultLineDatum, LineDatum, LineLayerId, LineSvgProps } from './types'
+import { svgDefaultProps } from './defaults'
import { useLine } from './hooks'
-import { LinePropTypes, LineDefaultProps } from './props'
-import Areas from './Areas'
-import Lines from './Lines'
-import Slices from './Slices'
-import Points from './Points'
-import Mesh from './Mesh'
-
-const Line = props => {
+import { Areas } from './Areas'
+import { Lines } from './Lines'
+import { Slices } from './Slices'
+import { Points } from './Points'
+import { Mesh } from './Mesh'
+
+type InnerLineProps = Omit<
+ LineSvgProps,
+ 'animate' | 'motionConfig' | 'renderWrapper' | 'theme'
+>
+
+const InnerLine = (
+ props: InnerLineProps
+) => {
const {
data,
- xScale: xScaleSpec,
+ xScale: xScaleSpec = svgDefaultProps.xScale,
xFormat,
- yScale: yScaleSpec,
+ yScale: yScaleSpec = svgDefaultProps.yScale,
yFormat,
- layers,
- curve,
- areaBaselineValue,
+ layers = svgDefaultProps.layers,
- colors,
+ colors = svgDefaultProps.colors,
margin: partialMargin,
width,
height,
- axisTop,
- axisRight,
- axisBottom,
- axisLeft,
- enableGridX,
- enableGridY,
+ enableGridX = svgDefaultProps.enableGridX,
gridXValues,
+ enableGridY = svgDefaultProps.enableGridY,
gridYValues,
+ axisTop = svgDefaultProps.axisTop,
+ axisRight = svgDefaultProps.axisRight,
+ axisBottom = svgDefaultProps.axisBottom,
+ axisLeft = svgDefaultProps.axisLeft,
- lineWidth,
- enableArea,
- areaOpacity,
- areaBlendMode,
+ curve = svgDefaultProps.curve,
+ lineWidth = svgDefaultProps.lineWidth,
+ enableArea = svgDefaultProps.enableArea,
+ areaBaselineValue = svgDefaultProps.areaBaselineValue,
+ areaOpacity = svgDefaultProps.areaOpacity,
+ areaBlendMode = svgDefaultProps.areaBlendMode,
- enablePoints,
+ enablePoints = svgDefaultProps.enablePoints,
pointSymbol,
- pointSize,
+ pointSize = svgDefaultProps.pointSize,
pointColor,
- pointBorderWidth,
+ pointBorderWidth = svgDefaultProps.pointBorderWidth,
pointBorderColor,
- enablePointLabel,
+ enablePointLabel = svgDefaultProps.enablePointLabel,
pointLabel,
pointLabelYOffset,
- defs,
- fill,
-
- markers,
-
- legends,
+ defs = svgDefaultProps.defs,
+ fill = svgDefaultProps.fill,
- isInteractive,
+ markers = svgDefaultProps.markers,
- useMesh,
- debugMesh,
+ legends = svgDefaultProps.legends,
+ isInteractive = svgDefaultProps.isInteractive,
+ useMesh = svgDefaultProps.useMesh,
+ debugMesh = svgDefaultProps.debugMesh,
+ enableSlices = svgDefaultProps.enableSlices,
+ debugSlices = svgDefaultProps.debugSlices,
+ sliceTooltip = svgDefaultProps.sliceTooltip,
+ enableCrosshair = svgDefaultProps.enableCrosshair,
+ crosshairType = svgDefaultProps.crosshairType,
onMouseEnter,
onMouseMove,
onMouseLeave,
onClick,
-
- tooltip,
-
- enableSlices,
- debugSlices,
- sliceTooltip,
-
- enableCrosshair,
- crosshairType,
+ tooltip = svgDefaultProps.tooltip,
role,
+ ariaLabel,
+ ariaLabelledBy,
+ ariaDescribedBy,
} = props
const { margin, innerWidth, innerHeight, outerWidth, outerHeight } = useDimensions(
@@ -112,8 +111,12 @@ const Line = props => {
xScale,
yScale,
slices,
+ currentSlice,
+ setCurrentSlice,
points,
- } = useLine({
+ currentPoint,
+ setCurrentPoint,
+ } = useLine({
data,
xScale: xScaleSpec,
xFormat,
@@ -129,15 +132,31 @@ const Line = props => {
enableSlices,
})
+ series.forEach(serie => {
+ console.log(serie)
+ serie.data.forEach(datum => {
+ console.log(datum)
+ })
+ })
+
const theme = useTheme()
- const getPointColor = useInheritedColor(pointColor, theme)
const getPointBorderColor = useInheritedColor(pointBorderColor, theme)
- const [currentPoint, setCurrentPoint] = useState(null)
- const [currentSlice, setCurrentSlice] = useState(null)
+ const layerById: Record = {
+ grid: null,
+ markers: null,
+ axes: null,
+ areas: null,
+ crosshair: null,
+ lines: null,
+ slices: null,
+ points: null,
+ mesh: null,
+ legends: null,
+ }
- const layerById = {
- grid: (
+ if (layers.includes('grid')) {
+ layerById.grid = (
{
xValues={gridXValues}
yValues={gridYValues}
/>
- ),
- markers: (
+ )
+ }
+
+ if (markers.length > 0 && layers.includes('markers')) {
+ layerById.markers = (
{
yScale={yScale}
theme={theme}
/>
- ),
- axes: (
+ )
+ }
+
+ if (layers.includes('axes')) {
+ layerById.axes = (
{
bottom={axisBottom}
left={axisLeft}
/>
- ),
- areas: null,
- lines: (
-
- ),
- slices: null,
- points: null,
- crosshair: null,
- mesh: null,
- legends: legends.map((legend, i) => (
-
- )),
+ )
}
- const boundDefs = bindDefs(defs, series, fill)
-
- if (enableArea) {
+ if (layers.includes('areas') && enableArea) {
layerById.areas = (
-
key="areas"
areaGenerator={areaGenerator}
areaOpacity={areaOpacity}
@@ -209,14 +213,24 @@ const Line = props => {
)
}
- if (isInteractive && enableSlices !== false) {
+ if (layers.includes('lines')) {
+ layerById.lines = (
+
+ key="lines"
+ lines={series}
+ lineGenerator={lineGenerator}
+ lineWidth={lineWidth}
+ />
+ )
+ }
+
+ if (isInteractive && enableSlices && layers.includes('slices')) {
layerById.slices = (
{
)
}
- if (enablePoints) {
+ if (enablePoints && layers.includes('points')) {
layerById.points = (
-
key="points"
points={points}
symbol={pointSymbol}
size={pointSize}
- color={getPointColor}
borderWidth={pointBorderWidth}
borderColor={getPointBorderColor}
enableLabel={enablePointLabel}
@@ -241,9 +254,50 @@ const Line = props => {
)
}
+ if (isInteractive && useMesh && !enableSlices && layers.includes('mesh')) {
+ layerById.mesh = (
+
+ key="mesh"
+ points={points}
+ width={innerWidth}
+ height={innerHeight}
+ margin={margin}
+ setCurrent={setCurrentPoint}
+ onMouseEnter={onMouseEnter}
+ onMouseMove={onMouseMove}
+ onMouseLeave={onMouseLeave}
+ onClick={onClick}
+ tooltip={tooltip}
+ debug={debugMesh}
+ />
+ )
+ }
+
+ if (layers.includes('legends') && legends.length > 0) {
+ layerById.legends = (
+
+ {legends.map((legend, legendIndex) => (
+
+ ))}
+
+ )
+ }
+
+ const ___layerById = {}
+
+ const boundDefs = bindDefs(defs, series, fill)
+
if (isInteractive && enableCrosshair) {
if (currentPoint !== null) {
- layerById.crosshair = (
+ ___layerById.crosshair = (
{
)
}
if (currentSlice !== null) {
- layerById.crosshair = (
+ ___layerById.crosshair = (
{
}
}
- if (isInteractive && useMesh && enableSlices === false) {
- layerById.mesh = (
-
- )
- }
-
return (
{
height={outerHeight}
margin={margin}
role={role}
+ ariaLabel={ariaLabel}
+ ariaLabelledBy={ariaLabelledBy}
+ ariaDescribedBy={ariaDescribedBy}
>
- {layers.map((layer, i) => {
+ {layers.map((layer, layerIndex) => {
if (typeof layer === 'function') {
return (
-
+
{layer({
...props,
innerWidth,
@@ -320,13 +357,32 @@ const Line = props => {
)
}
- return layerById[layer]
+ return layerById?.[layer] ?? null
})}
)
}
-Line.propTypes = LinePropTypes
-Line.defaultProps = LineDefaultProps
-
-export default withContainer(Line)
+export const Line = <
+ Datum extends LineDatum = DefaultLineDatum,
+ ExtraProps extends object = Record
+>({
+ isInteractive = svgDefaultProps.isInteractive,
+ animate = svgDefaultProps.animate,
+ motionConfig = svgDefaultProps.motionConfig,
+ theme,
+ renderWrapper,
+ ...otherProps
+}: LineSvgProps) => (
+
+ isInteractive={isInteractive} {...otherProps} />
+
+)
diff --git a/packages/line/src/LineCanvas.js b/packages/line/src/LineCanvas.tsx
similarity index 97%
rename from packages/line/src/LineCanvas.js
rename to packages/line/src/LineCanvas.tsx
index d47660d152..60db3aed38 100644
--- a/packages/line/src/LineCanvas.js
+++ b/packages/line/src/LineCanvas.tsx
@@ -1,11 +1,3 @@
-/*
- * This file is part of the nivo project.
- *
- * Copyright 2016-present, Raphaël Benitte.
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
import { createElement, useRef, useEffect, useState, useCallback, forwardRef } from 'react'
import {
withContainer,
diff --git a/packages/line/src/Lines.js b/packages/line/src/Lines.js
deleted file mode 100644
index 8e38885c0f..0000000000
--- a/packages/line/src/Lines.js
+++ /dev/null
@@ -1,60 +0,0 @@
-/*
- * This file is part of the nivo project.
- *
- * Copyright 2016-present, Raphaël Benitte.
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-import { memo } from 'react'
-import PropTypes from 'prop-types'
-import LinesItem from './LinesItem'
-
-const Lines = ({ lines, lineGenerator, lineWidth }) => {
- return lines
- .slice(0)
- .reverse()
- .map(({ id, data, color }) => (
- d.position)}
- lineGenerator={lineGenerator}
- color={color}
- thickness={lineWidth}
- />
- ))
-}
-
-Lines.propTypes = {
- lines: PropTypes.arrayOf(
- PropTypes.shape({
- id: PropTypes.oneOfType([PropTypes.string, PropTypes.number]).isRequired,
- color: PropTypes.string.isRequired,
- data: PropTypes.arrayOf(
- PropTypes.shape({
- data: PropTypes.shape({
- x: PropTypes.oneOfType([
- PropTypes.string,
- PropTypes.number,
- PropTypes.instanceOf(Date),
- ]),
- y: PropTypes.oneOfType([
- PropTypes.string,
- PropTypes.number,
- PropTypes.instanceOf(Date),
- ]),
- }).isRequired,
- position: PropTypes.shape({
- x: PropTypes.number,
- y: PropTypes.number,
- }).isRequired,
- })
- ).isRequired,
- })
- ).isRequired,
- lineWidth: PropTypes.number.isRequired,
- lineGenerator: PropTypes.func.isRequired,
-}
-
-export default memo(Lines)
diff --git a/packages/line/src/Lines.tsx b/packages/line/src/Lines.tsx
new file mode 100644
index 0000000000..2c91d3ad22
--- /dev/null
+++ b/packages/line/src/Lines.tsx
@@ -0,0 +1,30 @@
+import { memo } from 'react'
+import { LineDatum, LineGenerator } from './types'
+import { LinesItem } from './LinesItem'
+
+const NonMemoizedLines = ({
+ lines,
+ lineGenerator,
+ lineWidth,
+}: {
+ lines: any[]
+ lineGenerator: LineGenerator
+ lineWidth: number
+}) => (
+ <>
+ {lines
+ .slice(0)
+ .reverse()
+ .map(({ id, data, color }) => (
+
+ key={id}
+ points={data.map(d => d.position)}
+ lineGenerator={lineGenerator}
+ color={color}
+ thickness={lineWidth}
+ />
+ ))}
+ >
+)
+
+export const Lines = memo(NonMemoizedLines) as typeof NonMemoizedLines
diff --git a/packages/line/src/LinesItem.js b/packages/line/src/LinesItem.js
deleted file mode 100644
index a85c6b6989..0000000000
--- a/packages/line/src/LinesItem.js
+++ /dev/null
@@ -1,33 +0,0 @@
-/*
- * This file is part of the nivo project.
- *
- * Copyright 2016-present, Raphaël Benitte.
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-import { memo, useMemo } from 'react'
-import PropTypes from 'prop-types'
-import { animated } from '@react-spring/web'
-import { useAnimatedPath } from '@nivo/core'
-
-const LinesItem = ({ lineGenerator, points, color, thickness }) => {
- const path = useMemo(() => lineGenerator(points), [lineGenerator, points])
- const animatedPath = useAnimatedPath(path)
-
- return
-}
-
-LinesItem.propTypes = {
- points: PropTypes.arrayOf(
- PropTypes.shape({
- x: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
- y: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
- })
- ),
- lineGenerator: PropTypes.func.isRequired,
- color: PropTypes.string.isRequired,
- thickness: PropTypes.number.isRequired,
-}
-
-export default memo(LinesItem)
diff --git a/packages/line/src/LinesItem.tsx b/packages/line/src/LinesItem.tsx
new file mode 100644
index 0000000000..bedc3468d1
--- /dev/null
+++ b/packages/line/src/LinesItem.tsx
@@ -0,0 +1,23 @@
+import { memo, useMemo } from 'react'
+import { animated } from '@react-spring/web'
+import { useAnimatedPath } from '@nivo/core'
+import { LineDatum, LineGenerator, LinePointDatum } from './types'
+
+const NonMemoizedLinesItem = ({
+ lineGenerator,
+ points,
+ color,
+ thickness,
+}: {
+ lineGenerator: LineGenerator
+ points: LinePointDatum[]
+ color: string
+ thickness: number
+}) => {
+ const path = useMemo(() => lineGenerator(points), [lineGenerator, points])
+ const animatedPath = useAnimatedPath(path!)
+
+ return
+}
+
+export const LinesItem = memo(NonMemoizedLinesItem) as typeof NonMemoizedLinesItem
diff --git a/packages/line/src/Mesh.js b/packages/line/src/Mesh.tsx
similarity index 70%
rename from packages/line/src/Mesh.js
rename to packages/line/src/Mesh.tsx
index b04b5e38a1..3139c09cb2 100644
--- a/packages/line/src/Mesh.js
+++ b/packages/line/src/Mesh.tsx
@@ -1,17 +1,10 @@
-/*
- * This file is part of the nivo project.
- *
- * Copyright 2016-present, Raphaël Benitte.
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
import { createElement, memo, useCallback } from 'react'
-import PropTypes from 'prop-types'
+import { Margin } from '@nivo/core'
import { useTooltip } from '@nivo/tooltip'
import { Mesh as BaseMesh } from '@nivo/voronoi'
+import { LinePointDatum, LineDatum, LineCommonProps } from './types'
-const Mesh = ({
+const NonMemoizedMesh = ({
points,
width,
height,
@@ -23,6 +16,18 @@ const Mesh = ({
onClick,
tooltip,
debug,
+}: {
+ points: LinePointDatum[]
+ width: number
+ height: number
+ margin: Margin
+ setCurrent: (point: LinePointDatum | null) => void
+ onMouseEnter?: LineCommonProps['onMouseEnter']
+ onMouseMove?: LineCommonProps['onMouseMove']
+ onMouseLeave?: LineCommonProps['onMouseLeave']
+ onClick?: LineCommonProps['onClick']
+ tooltip: LineCommonProps['tooltip']
+ debug: LineCommonProps['debugMesh']
}) => {
const { showTooltipAt, hideTooltip } = useTooltip()
@@ -82,18 +87,4 @@ const Mesh = ({
)
}
-Mesh.propTypes = {
- points: PropTypes.arrayOf(PropTypes.object).isRequired,
- width: PropTypes.number.isRequired,
- height: PropTypes.number.isRequired,
- margin: PropTypes.object.isRequired,
- setCurrent: PropTypes.func.isRequired,
- onMouseEnter: PropTypes.func,
- onMouseMove: PropTypes.func,
- onMouseLeave: PropTypes.func,
- onClick: PropTypes.func,
- tooltip: PropTypes.oneOfType([PropTypes.func, PropTypes.object]).isRequired,
- debug: PropTypes.bool.isRequired,
-}
-
-export default memo(Mesh)
+export const Mesh = memo(NonMemoizedMesh) as typeof NonMemoizedMesh
diff --git a/packages/line/src/PointTooltip.js b/packages/line/src/PointTooltip.js
deleted file mode 100644
index 6436ad6aa4..0000000000
--- a/packages/line/src/PointTooltip.js
+++ /dev/null
@@ -1,32 +0,0 @@
-/*
- * This file is part of the nivo project.
- *
- * Copyright 2016-present, Raphaël Benitte.
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-import { memo } from 'react'
-import PropTypes from 'prop-types'
-import { BasicTooltip } from '@nivo/tooltip'
-
-const LinePointTooltip = ({ point }) => {
- return (
-
- x: {point.data.xFormatted}, y:{' '}
- {point.data.yFormatted}
-
- }
- enableChip={true}
- color={point.serieColor}
- />
- )
-}
-
-LinePointTooltip.propTypes = {
- point: PropTypes.object.isRequired,
-}
-
-export default memo(LinePointTooltip)
diff --git a/packages/line/src/PointTooltip.tsx b/packages/line/src/PointTooltip.tsx
new file mode 100644
index 0000000000..e9dd2bb0b3
--- /dev/null
+++ b/packages/line/src/PointTooltip.tsx
@@ -0,0 +1,24 @@
+import { memo } from 'react'
+import { BasicTooltip } from '@nivo/tooltip'
+import { LineDatum, LinePointDatum } from './types'
+
+interface LinePointTooltipProps {
+ point: LinePointDatum
+}
+
+const NonMemoizedPointTooltip = ({
+ point,
+}: LinePointTooltipProps) => (
+
+ x: {point.data.xFormatted}, y:{' '}
+ {point.data.yFormatted}
+
+ }
+ enableChip={true}
+ color={point.serieColor}
+ />
+)
+
+export const PointTooltip = memo(NonMemoizedPointTooltip) as typeof NonMemoizedPointTooltip
diff --git a/packages/line/src/Points.js b/packages/line/src/Points.tsx
similarity index 79%
rename from packages/line/src/Points.js
rename to packages/line/src/Points.tsx
index 09fcf15b7d..3c95f5ffc3 100644
--- a/packages/line/src/Points.js
+++ b/packages/line/src/Points.tsx
@@ -1,16 +1,24 @@
-/*
- * This file is part of the nivo project.
- *
- * Copyright 2016-present, Raphaël Benitte.
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
import { memo } from 'react'
-import PropTypes from 'prop-types'
import { getLabelGenerator, DotsItem, useTheme } from '@nivo/core'
+import { LinePointDatum, LineDatum } from './types'
-const Points = ({ points, symbol, size, borderWidth, enableLabel, label, labelYOffset }) => {
+const NonMemoizedPoints = ({
+ points,
+ symbol,
+ size,
+ borderWidth,
+ enableLabel,
+ label,
+ labelYOffset,
+}: {
+ points: LinePointDatum[]
+ symbol: any
+ size: number
+ borderWidth: number
+ enableLabel: boolean
+ label: any
+ labelYOffset: number
+}) => {
const theme = useTheme()
const getLabel = getLabelGenerator(label)
@@ -54,6 +62,7 @@ const Points = ({ points, symbol, size, borderWidth, enableLabel, label, labelYO
)
}
+/*
Points.propTypes = {
points: PropTypes.arrayOf(PropTypes.object),
symbol: PropTypes.func,
@@ -65,5 +74,6 @@ Points.propTypes = {
label: PropTypes.oneOfType([PropTypes.string, PropTypes.func]).isRequired,
labelYOffset: PropTypes.number,
}
+*/
-export default memo(Points)
+export const Points = memo(NonMemoizedPoints) as typeof NonMemoizedPoints
diff --git a/packages/line/src/ResponsiveLine.js b/packages/line/src/ResponsiveLine.js
deleted file mode 100644
index dfa5ce089d..0000000000
--- a/packages/line/src/ResponsiveLine.js
+++ /dev/null
@@ -1,18 +0,0 @@
-/*
- * This file is part of the nivo project.
- *
- * Copyright 2016-present, Raphaël Benitte.
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-import { ResponsiveWrapper } from '@nivo/core'
-import Line from './Line'
-
-const ResponsiveLine = props => (
-
- {({ width, height }) => }
-
-)
-
-export default ResponsiveLine
diff --git a/packages/line/src/ResponsiveLine.tsx b/packages/line/src/ResponsiveLine.tsx
new file mode 100644
index 0000000000..14bf5675dd
--- /dev/null
+++ b/packages/line/src/ResponsiveLine.tsx
@@ -0,0 +1,16 @@
+import { ResponsiveWrapper } from '@nivo/core'
+import { DefaultLineDatum, LineDatum, LineSvgProps } from './types'
+import { Line } from './Line'
+
+export const ResponsiveLine = <
+ Datum extends LineDatum = DefaultLineDatum,
+ ExtraProps extends object = Record
+>(
+ props: Omit, 'height' | 'width'>
+) => (
+
+ {({ width, height }) => (
+ width={width} height={height} {...props} />
+ )}
+
+)
diff --git a/packages/line/src/ResponsiveLineCanvas.js b/packages/line/src/ResponsiveLineCanvas.js
deleted file mode 100644
index 05b99f18d4..0000000000
--- a/packages/line/src/ResponsiveLineCanvas.js
+++ /dev/null
@@ -1,19 +0,0 @@
-/*
- * This file is part of the nivo project.
- *
- * Copyright 2016-present, Raphaël Benitte.
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-import { forwardRef } from 'react'
-import { ResponsiveWrapper } from '@nivo/core'
-import LineCanvas from './LineCanvas'
-
-const ResponsiveLineCanvas = (props, ref) => (
-
- {({ width, height }) => }
-
-)
-
-export default forwardRef(ResponsiveLineCanvas)
diff --git a/packages/line/src/ResponsiveLineCanvas.tsx b/packages/line/src/ResponsiveLineCanvas.tsx
new file mode 100644
index 0000000000..1ef3e63f06
--- /dev/null
+++ b/packages/line/src/ResponsiveLineCanvas.tsx
@@ -0,0 +1,18 @@
+import { ResponsiveWrapper } from '@nivo/core'
+import { DefaultLineDatum, LineDatum, LineCanvasProps } from './types'
+import LineCanvas from './LineCanvas'
+
+export const ResponsiveLineCanvas = <
+ Datum extends LineDatum = DefaultLineDatum,
+ ExtraProps extends object = Record
+>(
+ props: Omit, 'height' | 'width'>
+) => (
+
+ {({ width, height }) => (
+ width={width} height={height} {...props} />
+ )}
+
+)
+
+// export default forwardRef(ResponsiveLineCanvas)
diff --git a/packages/line/src/SliceTooltip.js b/packages/line/src/SliceTooltip.tsx
similarity index 56%
rename from packages/line/src/SliceTooltip.js
rename to packages/line/src/SliceTooltip.tsx
index c570f22745..0fdff0a7e6 100644
--- a/packages/line/src/SliceTooltip.js
+++ b/packages/line/src/SliceTooltip.tsx
@@ -1,17 +1,12 @@
-/*
- * This file is part of the nivo project.
- *
- * Copyright 2016-present, Raphaël Benitte.
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
import { memo } from 'react'
-import PropTypes from 'prop-types'
import { useTheme } from '@nivo/core'
import { Chip, TableTooltip } from '@nivo/tooltip'
+import { LineDatum, SliceTooltipProps } from './types'
-const SliceTooltip = ({ slice, axis }) => {
+const NonMemoizedSliceTooltip = ({
+ slice,
+ axis,
+}: SliceTooltipProps) => {
const theme = useTheme()
const otherAxis = axis === 'x' ? 'y' : 'x'
@@ -28,9 +23,4 @@ const SliceTooltip = ({ slice, axis }) => {
)
}
-SliceTooltip.propTypes = {
- slice: PropTypes.object.isRequired,
- axis: PropTypes.oneOf(['x', 'y']).isRequired,
-}
-
-export default memo(SliceTooltip)
+export const SliceTooltip = memo(NonMemoizedSliceTooltip) as typeof NonMemoizedSliceTooltip
diff --git a/packages/line/src/Slices.js b/packages/line/src/Slices.js
deleted file mode 100644
index 952688cfee..0000000000
--- a/packages/line/src/Slices.js
+++ /dev/null
@@ -1,49 +0,0 @@
-/*
- * This file is part of the nivo project.
- *
- * Copyright 2016-present, Raphaël Benitte.
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-import { memo } from 'react'
-import PropTypes from 'prop-types'
-import SlicesItem from './SlicesItem'
-
-const Slices = ({ slices, axis, debug, height, tooltip, current, setCurrent }) => {
- return slices.map(slice => (
-
- ))
-}
-
-Slices.propTypes = {
- slices: PropTypes.arrayOf(
- PropTypes.shape({
- id: PropTypes.oneOfType([
- PropTypes.number,
- PropTypes.string,
- PropTypes.instanceOf(Date),
- ]).isRequired,
- x: PropTypes.number.isRequired,
- y: PropTypes.number.isRequired,
- points: PropTypes.arrayOf(PropTypes.object).isRequired,
- })
- ).isRequired,
- axis: PropTypes.oneOf(['x', 'y']).isRequired,
- debug: PropTypes.bool.isRequired,
- height: PropTypes.number.isRequired,
- tooltip: PropTypes.oneOfType([PropTypes.func, PropTypes.object]).isRequired,
- current: PropTypes.object,
- setCurrent: PropTypes.func.isRequired,
-}
-
-export default memo(Slices)
diff --git a/packages/line/src/Slices.tsx b/packages/line/src/Slices.tsx
new file mode 100644
index 0000000000..4e0e60cf52
--- /dev/null
+++ b/packages/line/src/Slices.tsx
@@ -0,0 +1,35 @@
+import { memo } from 'react'
+import { LineCommonProps, LineDatum, SliceDatum } from './types'
+import { SlicesItem } from './SlicesItem'
+
+const NonMemoizedSlices = ({
+ slices,
+ axis,
+ debug,
+ tooltip,
+ current,
+ setCurrent,
+}: {
+ slices: SliceDatum[]
+ axis: Exclude['enableSlices'], false>
+ debug: boolean
+ tooltip: LineCommonProps['sliceTooltip']
+ current: SliceDatum | null
+ setCurrent: (slice: SliceDatum | null) => void
+}) => (
+ <>
+ {slices.map(slice => (
+
+ key={slice.id}
+ slice={slice}
+ axis={axis}
+ debug={debug}
+ tooltip={tooltip}
+ setCurrent={setCurrent}
+ isCurrent={current !== null && current.id === slice.id}
+ />
+ ))}
+ >
+)
+
+export const Slices = memo(NonMemoizedSlices) as typeof NonMemoizedSlices
diff --git a/packages/line/src/SlicesItem.js b/packages/line/src/SlicesItem.tsx
similarity index 62%
rename from packages/line/src/SlicesItem.js
rename to packages/line/src/SlicesItem.tsx
index fb01ca26c3..4c5185e6bc 100644
--- a/packages/line/src/SlicesItem.js
+++ b/packages/line/src/SlicesItem.tsx
@@ -1,16 +1,22 @@
-/*
- * This file is part of the nivo project.
- *
- * Copyright 2016-present, Raphaël Benitte.
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
import { createElement, memo, useCallback } from 'react'
-import PropTypes from 'prop-types'
import { useTooltip } from '@nivo/tooltip'
+import { LineCommonProps, LineDatum, SliceAxis, SliceDatum } from './types'
-const SlicesItem = ({ slice, axis, debug, tooltip, isCurrent, setCurrent }) => {
+const NonMemoizedSlicesItem = ({
+ slice,
+ axis,
+ debug,
+ tooltip,
+ isCurrent,
+ setCurrent,
+}: {
+ slice: SliceDatum
+ axis: SliceAxis
+ debug: boolean
+ tooltip: LineCommonProps['sliceTooltip']
+ isCurrent: boolean
+ setCurrent: (slice: SliceDatum | null) => void
+}) => {
const { showTooltipFromEvent, hideTooltip } = useTooltip()
const handleMouseEnter = useCallback(
@@ -51,14 +57,4 @@ const SlicesItem = ({ slice, axis, debug, tooltip, isCurrent, setCurrent }) => {
)
}
-SlicesItem.propTypes = {
- slice: PropTypes.object.isRequired,
- axis: PropTypes.oneOf(['x', 'y']).isRequired,
- debug: PropTypes.bool.isRequired,
- height: PropTypes.number.isRequired,
- tooltip: PropTypes.oneOfType([PropTypes.func, PropTypes.object]),
- isCurrent: PropTypes.bool.isRequired,
- setCurrent: PropTypes.func.isRequired,
-}
-
-export default memo(SlicesItem)
+export const SlicesItem = memo(NonMemoizedSlicesItem) as typeof NonMemoizedSlicesItem
diff --git a/packages/line/src/defaults.ts b/packages/line/src/defaults.ts
new file mode 100644
index 0000000000..7a16820ebc
--- /dev/null
+++ b/packages/line/src/defaults.ts
@@ -0,0 +1,108 @@
+import { DefaultLineDatum, LineCommonProps, LineLayerId } from './types'
+import { SliceTooltip } from './SliceTooltip'
+import { PointTooltip } from './PointTooltip'
+
+export const commonDefaultProps: Omit<
+ LineCommonProps,
+ | 'margin'
+ | 'theme'
+ | 'valueFormat'
+ | 'onClick'
+ | 'renderWrapper'
+ | 'role'
+ | 'ariaLabel'
+ | 'ariaLabelledBy'
+ | 'ariaDescribedBy'
+> & {
+ layers: LineLayerId[]
+} = {
+ layers: [
+ 'grid',
+ 'markers',
+ 'axes',
+ 'areas',
+ 'crosshair',
+ 'lines',
+ 'points',
+ 'slices',
+ 'mesh',
+ 'legends',
+ ] as LineLayerId[],
+
+ xScale: {
+ type: 'point',
+ },
+ yScale: {
+ type: 'linear',
+ min: 0,
+ max: 'auto',
+ },
+
+ enableGridX: false,
+ enableGridY: false,
+
+ curve: 'linear' as const,
+ lineWidth: 2,
+ enableArea: false,
+ areaBaselineValue: 0,
+ areaOpacity: 0.2,
+ areaBlendMode: 'normal' as const,
+
+ enablePoints: true,
+ pointSize: 6,
+ pointColor: { from: 'color' },
+ pointBorderWidth: 0,
+ pointBorderColor: { theme: 'background' },
+ enablePointLabel: false,
+ pointLabel: 'yFormatted',
+
+ colors: { scheme: 'nivo' },
+
+ opacity: 1,
+ activeOpacity: 1,
+ inactiveOpacity: 0.15,
+ borderWidth: 0,
+ borderColor: { from: 'color', modifiers: [['darker', 0.8]] },
+
+ enableLabels: true,
+ label: 'formattedValue',
+
+ legends: [],
+ markers: [],
+ annotations: [],
+
+ isInteractive: true,
+ debugMesh: false,
+ enableSlices: false,
+ debugSlices: false,
+ sliceTooltip: SliceTooltip,
+ enableCrosshair: true,
+ crosshairType: 'bottom-left' as const,
+ tooltip: PointTooltip,
+
+ animate: true,
+ motionConfig: 'gentle' as const,
+}
+
+export const svgDefaultProps = {
+ ...commonDefaultProps,
+ axisTop: {},
+ axisRight: null,
+ axisBottom: null,
+ axisLeft: {},
+ borderRadius: 0,
+ cellComponent: 'rect' as const,
+ defs: [],
+ fill: [],
+ useMesh: false,
+}
+
+export const canvasDefaultProps = {
+ ...commonDefaultProps,
+ axisTop: {},
+ axisRight: null,
+ axisBottom: null,
+ axisLeft: {},
+ renderCell: 'rect' as const,
+ pixelRatio: typeof window !== 'undefined' ? window.devicePixelRatio || 1 : 1,
+}
diff --git a/packages/line/src/hooks.js b/packages/line/src/hooks.ts
similarity index 54%
rename from packages/line/src/hooks.js
rename to packages/line/src/hooks.ts
index 93e6d0fa3e..eb685a17fc 100644
--- a/packages/line/src/hooks.js
+++ b/packages/line/src/hooks.ts
@@ -1,74 +1,110 @@
-/*
- * This file is part of the nivo project.
- *
- * Copyright 2016-present, Raphaël Benitte.
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
import { useCallback, useMemo, useState } from 'react'
import { area, line } from 'd3-shape'
import { curveFromProp, useTheme, useValueFormatter } from '@nivo/core'
import { useOrdinalColorScale, useInheritedColor } from '@nivo/colors'
import { computeXYScalesForSeries } from '@nivo/scales'
import { LineDefaultProps } from './props'
+import { commonDefaultProps } from './defaults'
+import {
+ CurveInterpolation,
+ LineCommonProps,
+ LineDataProps,
+ LineDatum,
+ LinePointDatum,
+ SliceDatum,
+} from './types'
-export const useLineGenerator = ({ curve }) => {
- return useMemo(
+export const useLineGenerator = ({
+ curve,
+}: {
+ curve: CurveInterpolation
+}) =>
+ useMemo(
() =>
- line()
+ line['position']>()
.defined(d => d.x !== null && d.y !== null)
.x(d => d.x)
.y(d => d.y)
.curve(curveFromProp(curve)),
[curve]
)
-}
-export const useAreaGenerator = ({ curve, yScale, areaBaselineValue }) => {
- return useMemo(() => {
- return area()
- .defined(d => d.x !== null && d.y !== null)
- .x(d => d.x)
- .y1(d => d.y)
- .curve(curveFromProp(curve))
- .y0(yScale(areaBaselineValue))
- }, [curve, yScale, areaBaselineValue])
-}
+export const useAreaGenerator = ({
+ curve,
+ yScale,
+ areaBaselineValue,
+}: {
+ curve: CurveInterpolation
+ yScale: any
+ areaBaselineValue: Exclude
+}) =>
+ useMemo(
+ () =>
+ area['position']>()
+ .defined(d => d.x !== null && d.y !== null)
+ .x(d => d.x)
+ .y1(d => d.y)
+ .curve(curveFromProp(curve))
+ .y0(yScale(areaBaselineValue)),
+ [curve, yScale, areaBaselineValue]
+ )
-const usePoints = ({ series, getPointColor, getPointBorderColor, formatX, formatY }) => {
- return useMemo(() => {
- return series.reduce((acc, serie) => {
- return [
- ...acc,
- ...serie.data
- .filter(datum => datum.position.x !== null && datum.position.y !== null)
- .map((datum, i) => {
- const point = {
- id: `${serie.id}.${i}`,
- index: acc.length + i,
- serieId: serie.id,
- serieColor: serie.color,
- x: datum.position.x,
- y: datum.position.y,
- }
- point.color = getPointColor(serie)
- point.borderColor = getPointBorderColor(point)
- point.data = {
- ...datum.data,
- xFormatted: formatX(datum.data.x),
- yFormatted: formatY(datum.data.y),
- }
-
- return point
- }),
- ]
- }, [])
+const usePoints = ({
+ series,
+ getPointColor,
+ getPointBorderColor,
+ formatX,
+ formatY,
+}: {
+ series: any[]
+ getPointColor: any
+ getPointBorderColor: any
+ formatX: any
+ formatY: any
+}): LinePointDatum[] =>
+ useMemo(() => {
+ const points: LinePointDatum[] = []
+
+ series.forEach(serie => {
+ serie.data
+ // exclude undefined values
+ .filter(datum => datum.position.x !== null && datum.position.y !== null)
+ .forEach((datum, datumIndex) => {
+ const point = {
+ id: `${serie.id}.${datumIndex}`,
+ index: points.length,
+ serieId: serie.id,
+ serieColor: serie.color,
+ x: datum.position.x,
+ y: datum.position.y,
+ }
+ point.color = getPointColor(serie)
+ point.borderColor = getPointBorderColor(point)
+ point.data = {
+ ...datum.data,
+ xFormatted: formatX(datum.data.x),
+ yFormatted: formatY(datum.data.y),
+ }
+
+ points.push(point)
+ })
+ })
+
+ return points
}, [series, getPointColor, getPointBorderColor, formatX, formatY])
-}
-export const useSlices = ({ enableSlices, points, width, height }) => {
- return useMemo(() => {
+export const useSlices = ({
+ enableSlices,
+ points,
+ width,
+ height,
+}: {
+ enableSlices: LineCommonProps['enableSlices']
+ points: LinePointDatum[]
+ width: number
+ height: number
+}) =>
+ useMemo(() => {
if (enableSlices === false) return []
if (enableSlices === 'x') {
@@ -137,30 +173,53 @@ export const useSlices = ({ enableSlices, points, width, height }) => {
})
}
}, [enableSlices, points])
-}
-export const useLine = ({
+export const useLine = ({
data,
- xScale: xScaleSpec = LineDefaultProps.xScale,
+ xScale: xScaleSpec = commonDefaultProps.xScale,
xFormat,
- yScale: yScaleSpec = LineDefaultProps.yScale,
+ yScale: yScaleSpec = commonDefaultProps.yScale,
yFormat,
width,
height,
colors = LineDefaultProps.colors,
- curve = LineDefaultProps.curve,
- areaBaselineValue = LineDefaultProps.areaBaselineValue,
+ curve = commonDefaultProps.curve,
+ areaBaselineValue = commonDefaultProps.areaBaselineValue,
pointColor = LineDefaultProps.pointColor,
pointBorderColor = LineDefaultProps.pointBorderColor,
- enableSlices = LineDefaultProps.enableSlicesTooltip,
+ enableSlices = LineDefaultProps.enableSlices,
+}: {
+ data: LineDataProps['data']
+ xScale?: LineCommonProps['xScale']
+ xFormat?: LineCommonProps['xFormat']
+ yScale?: LineCommonProps['yScale']
+ yFormat?: LineCommonProps['yFormat']
+ width: number
+ height: number
+ colors: any
+ curve?: CurveInterpolation
+ areaBaselineValue?: Exclude
+ pointColor: any
+ pointBorderColor: any
+ enableSlices?: LineCommonProps['enableSlices']
}) => {
const formatX = useValueFormatter(xFormat)
const formatY = useValueFormatter(yFormat)
+
const getColor = useOrdinalColorScale(colors, 'id')
const theme = useTheme()
const getPointColor = useInheritedColor(pointColor, theme)
const getPointBorderColor = useInheritedColor(pointBorderColor, theme)
- const [hiddenIds, setHiddenIds] = useState([])
+
+ const [hiddenIds, setHiddenIds] = useState([])
+ const toggleSerie = useCallback((id: string) => {
+ setHiddenIds(state =>
+ state.includes(id) ? state.filter(item => item !== id) : [...state, id]
+ )
+ }, [])
+
+ const [currentPoint, setCurrentPoint] = useState | null>(null)
+ const [currentSlice, setCurrentSlice] = useState | null>(null)
const {
xScale,
@@ -169,7 +228,7 @@ export const useLine = ({
} = useMemo(
() =>
computeXYScalesForSeries(
- data.filter(item => hiddenIds.indexOf(item.id) === -1),
+ data.filter(item => !hiddenIds.includes(item.id)),
xScaleSpec,
yScaleSpec,
width,
@@ -184,26 +243,25 @@ export const useLine = ({
label: line.id,
color: getColor(line),
}))
+
const series = dataWithColor
.map(datum => ({
...rawSeries.find(serie => serie.id === datum.id),
color: datum.color,
}))
.filter(item => Boolean(item.id))
- const legendData = dataWithColor
+
+ const _legendData = dataWithColor
.map(item => ({ ...item, hidden: !series.find(serie => serie.id === item.id) }))
.reverse()
- return { legendData, series }
+ return {
+ series,
+ legendData: _legendData,
+ }
}, [data, rawSeries, getColor])
- const toggleSerie = useCallback(id => {
- setHiddenIds(state =>
- state.indexOf(id) > -1 ? state.filter(item => item !== id) : [...state, id]
- )
- }, [])
-
- const points = usePoints({
+ const points = usePoints({
series,
getPointColor,
getPointBorderColor,
@@ -218,8 +276,11 @@ export const useLine = ({
height,
})
- const lineGenerator = useLineGenerator({ curve })
- const areaGenerator = useAreaGenerator({
+ console.log('points', points)
+ console.log('slices', slices)
+
+ const lineGenerator = useLineGenerator({ curve })
+ const areaGenerator = useAreaGenerator({
curve,
yScale,
areaBaselineValue,
@@ -235,6 +296,10 @@ export const useLine = ({
xScale,
yScale,
slices,
+ currentSlice,
+ setCurrentSlice,
points,
+ currentPoint,
+ setCurrentPoint,
}
}
diff --git a/packages/line/src/index.js b/packages/line/src/index.js
deleted file mode 100644
index bae4590d2b..0000000000
--- a/packages/line/src/index.js
+++ /dev/null
@@ -1,14 +0,0 @@
-/*
- * This file is part of the nivo project.
- *
- * Copyright 2016-present, Raphaël Benitte.
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-export { default as Line } from './Line'
-export { default as ResponsiveLine } from './ResponsiveLine'
-export { default as LineCanvas } from './LineCanvas'
-export { default as ResponsiveLineCanvas } from './ResponsiveLineCanvas'
-export * from './props'
-export * from './hooks'
diff --git a/packages/line/src/index.ts b/packages/line/src/index.ts
new file mode 100644
index 0000000000..a08ced2a95
--- /dev/null
+++ b/packages/line/src/index.ts
@@ -0,0 +1,7 @@
+export * from './Line'
+export * from './ResponsiveLine'
+export { default as LineCanvas } from './LineCanvas'
+export * from './ResponsiveLineCanvas'
+export * from './props'
+export * from './hooks'
+export * from './types'
diff --git a/packages/line/src/props.js b/packages/line/src/props.tsx
similarity index 94%
rename from packages/line/src/props.js
rename to packages/line/src/props.tsx
index b2eeaa9d75..5a9304095c 100644
--- a/packages/line/src/props.js
+++ b/packages/line/src/props.tsx
@@ -1,18 +1,10 @@
-/*
- * This file is part of the nivo project.
- *
- * Copyright 2016-present, Raphaël Benitte.
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
import PropTypes from 'prop-types'
import { lineCurvePropType, blendModePropType, motionPropTypes, defsPropTypes } from '@nivo/core'
import { ordinalColorsPropType } from '@nivo/colors'
import { axisPropType } from '@nivo/axes'
import { LegendPropShape } from '@nivo/legends'
-import PointTooltip from './PointTooltip'
-import SliceTooltip from './SliceTooltip'
+import { PointTooltip } from './PointTooltip'
+import { SliceTooltip } from './SliceTooltip'
const commonPropTypes = {
data: PropTypes.arrayOf(
@@ -190,7 +182,7 @@ const commonDefaultProps = {
isInteractive: true,
tooltip: PointTooltip,
- enableSlices: false,
+ enableSlices: false as const,
debugSlices: false,
sliceTooltip: SliceTooltip,
debugMesh: false,
diff --git a/packages/line/src/types.ts b/packages/line/src/types.ts
new file mode 100644
index 0000000000..bf95b19b32
--- /dev/null
+++ b/packages/line/src/types.ts
@@ -0,0 +1,209 @@
+import { AriaAttributes, MouseEvent, FunctionComponent } from 'react'
+import { Line, Area } from 'd3-shape'
+import {
+ Box,
+ Theme,
+ Dimensions,
+ ModernMotionProps,
+ PropertyAccessor,
+ SvgDefsAndFill,
+ CssMixBlendMode,
+ ValueFormat,
+} from '@nivo/core'
+import { AxisProps, CanvasAxisProps } from '@nivo/axes'
+import { CrosshairType } from '@nivo/tooltip'
+import { ScaleSpec } from '@nivo/scales'
+import { LegendProps } from '@nivo/legends'
+
+export type LineValue = number | string | Date | null
+
+export interface LineDatum {
+ x: LineValue
+ y: LineValue
+}
+
+export interface DefaultLineDatum {
+ x: number | null | undefined
+ y: number | null | undefined
+}
+
+export type LineSeries = {
+ id: string
+ data: Datum[]
+} & ExtraProps
+
+export type SliceAxis = 'x' | 'y'
+
+export interface SliceDatum {
+ id: 0
+ x0: number
+ x: number
+ y0: number
+ y: number
+ width: number
+ height: number
+ points: any[]
+}
+
+export interface PointTooltipProps {
+ point: LinePointDatum
+}
+export type PointTooltipComponent = FunctionComponent<
+ PointTooltipProps
+>
+
+export interface SliceTooltipProps {
+ slice: SliceDatum
+ axis: SliceAxis
+}
+export type SliceTooltipComponent = FunctionComponent<
+ SliceTooltipProps
+>
+
+export interface LineDataProps {
+ data: LineSeries[]
+}
+
+export type LineLayerId =
+ | 'grid'
+ | 'markers'
+ | 'axes'
+ | 'areas'
+ | 'crosshair'
+ | 'lines'
+ | 'slices'
+ | 'points'
+ | 'mesh'
+ | 'legends'
+
+export type CurveInterpolation =
+ | 'basis'
+ | 'cardinal'
+ | 'catmullRom'
+ | 'linear'
+ | 'monotoneX'
+ | 'monotoneY'
+ | 'natural'
+ | 'step'
+ | 'stepAfter'
+ | 'stepBefore'
+
+export type LineGenerator = Line['position']>
+
+export type AreaGenerator = Area['position']>
+
+export interface LineStyle {
+ lineWidth: number
+ opacity: number
+}
+
+export interface LineSvgStyle extends LineStyle {}
+
+export interface LineCanvasStyle extends LineStyle {}
+
+export interface LinePointDatum {
+ data: Datum
+ position: {
+ x: number
+ y: number
+ }
+}
+
+export interface PointDatum {
+ id: string
+ index: number
+ serieId: string
+ serieColor: string
+ x: number
+ y: number
+ data: Datum & {
+ xFormatted: string
+ yFormatted: string
+ }
+}
+
+export type LineCommonProps = {
+ xScale: ScaleSpec
+ xFormat: ValueFormat>
+ yScale: ScaleSpec
+ yFormat: ValueFormat>
+
+ margin: Box
+
+ layers: LineLayerId[]
+
+ enableGridX: boolean
+ gridXValues: Exclude[]
+ enableGridY: boolean
+ gridYValues: Exclude[]
+
+ curve: CurveInterpolation
+ lineWidth: number
+ enableArea: boolean
+ areaBaselineValue: number
+ areaOpacity: number
+ areaBlendMode: CssMixBlendMode
+
+ enablePoints: boolean
+ pointSize: number
+ // pointColor: { from: 'color' },
+ pointBorderWidth: number
+ // pointBorderColor: { theme: 'background' },
+ enablePointLabel: boolean
+ // pointLabel: 'yFormatted',
+
+ theme: Theme
+
+ // annotations: AnnotationMatcher>[]
+
+ isInteractive: boolean
+ debugMesh: boolean
+ enableSlices: SliceAxis | false
+ debugSlices: boolean
+ sliceTooltip: SliceTooltipComponent
+ enableCrosshair: boolean
+ crosshairType: CrosshairType
+ onMouseEnter: (point: LinePointDatum, event: MouseEvent) => void
+ onMouseMove: (point: LinePointDatum, event: MouseEvent) => void
+ onMouseLeave: (point: LinePointDatum, event: MouseEvent) => void
+ onClick: (point: LinePointDatum, event: MouseEvent) => void
+ tooltip: PointTooltipComponent
+
+ markers: any[]
+
+ legends: LegendProps[]
+
+ renderWrapper: boolean
+
+ role: string
+ ariaLabel: AriaAttributes['aria-label']
+ ariaLabelledBy: AriaAttributes['aria-labelledby']
+ ariaDescribedBy: AriaAttributes['aria-describedby']
+} & Required
+
+export type LineSvgProps = Partial<
+ LineCommonProps
+> &
+ LineDataProps &
+ Dimensions &
+ SvgDefsAndFill &
+ Partial<{
+ axisTop: AxisProps | null
+ axisRight: AxisProps | null
+ axisBottom: AxisProps | null
+ axisLeft: AxisProps | null
+ useMesh: boolean
+ }>
+
+export type LineCanvasProps = Partial<
+ LineCommonProps
+> &
+ LineDataProps &
+ Dimensions &
+ Partial<{
+ axisTop: CanvasAxisProps> | null
+ axisRight: CanvasAxisProps> | null
+ axisBottom: CanvasAxisProps> | null
+ axisLeft: CanvasAxisProps> | null
+ pixelRatio: number
+ }>
diff --git a/packages/line/stories/line.stories.js b/packages/line/stories/Line.stories.tsx
similarity index 98%
rename from packages/line/stories/line.stories.js
rename to packages/line/stories/Line.stories.tsx
index b0104b9196..77439d8119 100644
--- a/packages/line/stories/line.stories.js
+++ b/packages/line/stories/Line.stories.tsx
@@ -1,11 +1,3 @@
-/*
- * This file is part of the nivo project.
- *
- * Copyright 2016-present, Raphaël Benitte.
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
import { Component, useState, useEffect } from 'react'
import range from 'lodash/range'
import last from 'lodash/last'
@@ -16,7 +8,7 @@ import { Defs, linearGradientDef } from '@nivo/core'
import { area, curveMonotoneX } from 'd3-shape'
import * as time from 'd3-time'
import { timeFormat } from 'd3-time-format'
-import { Line } from '../src'
+import { Line, CurveInterpolation } from '../src'
const data = generateDrinkStats(18)
const commonProperties = {
@@ -25,10 +17,16 @@ const commonProperties = {
margin: { top: 20, right: 20, bottom: 60, left: 80 },
data,
animate: true,
- enableSlices: 'x',
+ enableSlices: 'x' as const,
}
-const curveOptions = ['linear', 'monotoneX', 'step', 'stepBefore', 'stepAfter']
+const curveOptions: CurveInterpolation[] = [
+ 'linear',
+ 'monotoneX',
+ 'step',
+ 'stepBefore',
+ 'stepAfter',
+]
const CustomSymbol = ({ size, color, borderWidth, borderColor }) => (
@@ -185,7 +183,10 @@ stories.add('time scale', () => (
))
stories.add('time scale milliseconds precision', () => (
-
{...commonProperties}
data={[
{
diff --git a/packages/line/stories/LineCanvas.stories.js b/packages/line/stories/LineCanvas.stories.js
index 695c2d0340..f8187e6e2b 100644
--- a/packages/line/stories/LineCanvas.stories.js
+++ b/packages/line/stories/LineCanvas.stories.js
@@ -1,11 +1,3 @@
-/*
- * This file is part of the nivo project.
- *
- * Copyright 2016-present, Raphaël Benitte.
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
import { useRef } from 'react'
import { storiesOf } from '@storybook/react'
import { withKnobs, boolean, button } from '@storybook/addon-knobs'
diff --git a/packages/line/stories/ResponsiveLineCanvas.stories.js b/packages/line/stories/ResponsiveLineCanvas.stories.js
index 8df4f1649f..bd03a82c9c 100644
--- a/packages/line/stories/ResponsiveLineCanvas.stories.js
+++ b/packages/line/stories/ResponsiveLineCanvas.stories.js
@@ -1,11 +1,3 @@
-/*
- * This file is part of the nivo project.
- *
- * Copyright 2016-present, Raphaël Benitte.
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
import { useRef } from 'react'
import { storiesOf } from '@storybook/react'
import { withKnobs, boolean, button } from '@storybook/addon-knobs'
diff --git a/packages/line/tests/Line.test.js b/packages/line/tests/Line.test.tsx
similarity index 56%
rename from packages/line/tests/Line.test.js
rename to packages/line/tests/Line.test.tsx
index 7aa0467776..f1172339ab 100644
--- a/packages/line/tests/Line.test.js
+++ b/packages/line/tests/Line.test.tsx
@@ -1,9 +1,104 @@
import { mount } from 'enzyme'
-import { Axis } from '@nivo/axes'
-import Line from '../src/Line'
-import SlicesItem from '../src/SlicesItem'
-import renderer from 'react-test-renderer'
+import { Line } from '../src'
+import { SlicesItem } from '../src/SlicesItem'
+interface TestDatum {
+ x: number
+ y: number
+}
+
+const sampleData = [
+ {
+ id: 'A',
+ data: [
+ { x: 0, y: 3 },
+ { x: 1, y: 7 },
+ { x: 2, y: 11 },
+ { x: 3, y: 9 },
+ { x: 4, y: 8 },
+ ],
+ },
+ {
+ id: 'B',
+ data: [
+ { x: 0, y: 1 },
+ { x: 1, y: 3 },
+ { x: 2, y: 5 },
+ { x: 3, y: 7 },
+ { x: 4, y: 11 },
+ ],
+ },
+]
+
+const baseProps = {
+ width: 500,
+ height: 300,
+ data: sampleData,
+ // this is important to be able to "easily"
+ // extract line points' position.
+ curve: 'linear' as const,
+ animate: false,
+}
+
+it('should render a basic line chart', () => {
+ const wrapper = mount( {...baseProps} />)
+ console.log(wrapper.debug())
+})
+
+describe('slices', () => {
+ it('should create slice for each x value', () => {
+ const data = [
+ {
+ id: 'A',
+ data: [
+ { x: 0, y: 3 },
+ { x: 1, y: 7 },
+ { x: 2, y: 11 },
+ { x: 3, y: 9 },
+ { x: 4, y: 8 },
+ ],
+ },
+ ]
+ const wrapper = mount(
+
+ width={500}
+ height={300}
+ data={data}
+ enableSlices="x"
+ animate={false}
+ />
+ )
+
+ const slices = wrapper.find(SlicesItem)
+ expect(slices).toHaveLength(5)
+ expect(slices.at(0).prop('slice').id).toBe(0)
+ expect(slices.at(1).prop('slice').id).toBe(125)
+ expect(slices.at(2).prop('slice').id).toBe(250)
+ expect(slices.at(3).prop('slice').id).toBe(375)
+ expect(slices.at(4).prop('slice').id).toBe(500)
+ })
+})
+
+describe('accessibility', () => {
+ it('should forward root aria properties to the SVG element', () => {
+ const wrapper = mount(
+
+ {...baseProps}
+ ariaLabel="AriaLabel"
+ ariaLabelledBy="AriaLabelledBy"
+ ariaDescribedBy="AriaDescribedBy"
+ />
+ )
+
+ const svg = wrapper.find('svg')
+
+ expect(svg.prop('aria-label')).toBe('AriaLabel')
+ expect(svg.prop('aria-labelledby')).toBe('AriaLabelledBy')
+ expect(svg.prop('aria-describedby')).toBe('AriaDescribedBy')
+ })
+})
+
+/*
it('should render a basic line chart', () => {
const data = [
{
@@ -53,29 +148,7 @@ it('should support multiple lines', () => {
})
it('should create slice for each x value', () => {
- const data = [
- {
- id: 'A',
- data: [
- { x: 0, y: 3 },
- { x: 1, y: 7 },
- { x: 2, y: 11 },
- { x: 3, y: 9 },
- { x: 4, y: 8 },
- ],
- },
- ]
- const wrapper = mount(
-
- )
-
- const slices = wrapper.find(SlicesItem)
- expect(slices).toHaveLength(5)
- expect(slices.at(0).prop('slice').id).toBe(0)
- expect(slices.at(1).prop('slice').id).toBe(125)
- expect(slices.at(2).prop('slice').id).toBe(250)
- expect(slices.at(3).prop('slice').id).toBe(375)
- expect(slices.at(4).prop('slice').id).toBe(500)
+
})
it('should have left and bottom axis by default', () => {
@@ -148,3 +221,4 @@ describe('curve interpolation', () => {
})
}
})
+*/
diff --git a/packages/line/tests/__snapshots__/Line.test.js.snap b/packages/line/tests/__snapshots__/Line.test.js.snap
deleted file mode 100644
index e8c22a85f3..0000000000
--- a/packages/line/tests/__snapshots__/Line.test.js.snap
+++ /dev/null
@@ -1,9565 +0,0 @@
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`curve interpolation should support basis curve interpolation 1`] = `
-
-
-
-`;
-
-exports[`curve interpolation should support cardinal curve interpolation 1`] = `
-
-
-
-`;
-
-exports[`curve interpolation should support catmullRom curve interpolation 1`] = `
-
-
-
-`;
-
-exports[`curve interpolation should support linear curve interpolation 1`] = `
-
-
-
-`;
-
-exports[`curve interpolation should support monotoneX curve interpolation 1`] = `
-
-
-
-`;
-
-exports[`curve interpolation should support monotoneY curve interpolation 1`] = `
-
-
-
-`;
-
-exports[`curve interpolation should support natural curve interpolation 1`] = `
-
-
-
-`;
-
-exports[`curve interpolation should support step curve interpolation 1`] = `
-
-
-
-`;
-
-exports[`curve interpolation should support stepAfter curve interpolation 1`] = `
-
-
-
-`;
-
-exports[`curve interpolation should support stepBefore curve interpolation 1`] = `
-
-
-
-`;
-
-exports[`should render a basic line chart 1`] = `
-
-
-
-`;
-
-exports[`should support multiple lines 1`] = `
-
-
-
-`;
diff --git a/packages/line/tsconfig.json b/packages/line/tsconfig.json
new file mode 100644
index 0000000000..855b4b2b74
--- /dev/null
+++ b/packages/line/tsconfig.json
@@ -0,0 +1,8 @@
+{
+ "extends": "../../tsconfig.types.json",
+ "compilerOptions": {
+ "outDir": "./dist/types",
+ "rootDir": "./src"
+ },
+ "include": ["src/**/*"]
+}
diff --git a/tsconfig.monorepo.json b/tsconfig.monorepo.json
index 99343d876a..134504bc7d 100644
--- a/tsconfig.monorepo.json
+++ b/tsconfig.monorepo.json
@@ -28,6 +28,7 @@
{ "path": "./packages/circle-packing" },
{ "path": "./packages/funnel" },
{ "path": "./packages/heatmap" },
+ { "path": "./packages/line" },
{ "path": "./packages/marimekko" },
{ "path": "./packages/network" },
{ "path": "./packages/pie" },