diff --git a/Makefile b/Makefile
index 80f3a18b42..cadcd815fe 100644
--- a/Makefile
+++ b/Makefile
@@ -118,6 +118,15 @@ package-build-watch-%: ##@packages build package (es flavor) on change, eg. `pac
@echo "${YELLOW}Running build watcher for package ${WHITE}${*}${RESET}"
@cd packages/nivo-${*} && yarn build:es:watch
+package-build-%: ##@packages build package (all flavors), eg. `package-build-bar`
+ @echo "${YELLOW}Build package ${WHITE}${*}${RESET}"
+ @cd packages/nivo-${*} && yarn build
+
+package-dev-%: ##@packages setup package for development, link to website, run watcher
+ @echo "${YELLOW}Preparing package ${WHITE}${*}${YELLOW} for development${RESET}"
+ @cd packages/nivo-${*} && yarn link
+ @cd website && yarn link @nivo/${*}
+ @make package-build-watch-${*}
########################################################################################################################
#
diff --git a/README.md b/README.md
index 5bd3924020..638e512f5e 100644
--- a/README.md
+++ b/README.md
@@ -117,10 +117,9 @@ Join the [nivo discord community](https://discord.gg/n7Ft74f).
## Repositories
-* [nivo](https://github.com/plouc/nivo) - the nivo library
+* [nivo](https://github.com/plouc/nivo) - nivo packages, website, storybook and examples
* [nivo-api](https://github.com/plouc/nivo-api) - the nivo http api
* [nivo-api-docker](https://github.com/plouc/nivo-api-docker) - a Docker image for the nivo http api
-* [nivo-generators](https://github.com/plouc/nivo-generators) - the data generators used for nivo-website and http API samples
## Credits
diff --git a/packages/nivo-bar/package.json b/packages/nivo-bar/package.json
index c15a0d099e..3329bcddf7 100644
--- a/packages/nivo-bar/package.json
+++ b/packages/nivo-bar/package.json
@@ -7,7 +7,6 @@
"jsnext:main": "es/index.js",
"dependencies": {
"@nivo/core": "0.32.0",
- "@nivo/legend": "0.32.0",
"d3-scale": "^1.0.6",
"d3-shape": "^1.2.0",
"react-motion": "^0.5.2",
diff --git a/packages/nivo-bar/src/Bar.js b/packages/nivo-bar/src/Bar.js
index 741faec978..0d60305826 100644
--- a/packages/nivo-bar/src/Bar.js
+++ b/packages/nivo-bar/src/Bar.js
@@ -14,7 +14,6 @@ import setDisplayName from 'recompose/setDisplayName'
import enhance from './enhance'
import { BarPropTypes } from './props'
import { Container, SvgWrapper } from '@nivo/core'
-import { BoxLegendSvg } from '@nivo/legends'
import { Grid, Axes } from '@nivo/core'
import { CartesianMarkers } from '@nivo/core'
@@ -255,120 +254,6 @@ const Bar = ({
yScale={result.yScale}
theme={theme}
/>
-
-
-
-
-
-
-
-
-
-
-
-
)
}}
diff --git a/packages/nivo-calendar/package.json b/packages/nivo-calendar/package.json
index aa0f15c3be..7449d84635 100644
--- a/packages/nivo-calendar/package.json
+++ b/packages/nivo-calendar/package.json
@@ -7,6 +7,7 @@
"jsnext:main": "es/index.js",
"dependencies": {
"@nivo/core": "0.32.0",
+ "@nivo/legends": "0.32.0",
"d3-scale": "^1.0.6",
"d3-time": "^1.0.7",
"d3-time-format": "^2.0.5",
diff --git a/packages/nivo-calendar/src/Calendar.js b/packages/nivo-calendar/src/Calendar.js
index 59538a028d..58e211e5c6 100644
--- a/packages/nivo-calendar/src/Calendar.js
+++ b/packages/nivo-calendar/src/Calendar.js
@@ -7,9 +7,10 @@
* file that was distributed with this source code.
*/
import React from 'react'
+import { timeFormat } from 'd3-time-format'
+import { BoxLegendSvg } from '@nivo/legends'
import computeCalendar from './computeCalendar'
import { CalendarPropTypes } from './props'
-import { timeFormat } from 'd3-time-format'
import { DIRECTION_HORIZONTAL } from './constants'
import CalendarDay from './CalendarDay'
import CalendarMonthPath from './CalendarMonthPath'
@@ -49,6 +50,8 @@ const Calendar = ({
isInteractive,
tooltipFormat,
onClick,
+
+ legends,
}) => {
const { years, months, days } = computeCalendar({
width,
@@ -135,6 +138,22 @@ const Calendar = ({
)
})}
+ {legends.map((legend, i) => {
+ const legendData = colorScale.ticks(legend.itemCount).map(value => ({
+ label: value,
+ fill: colorScale(value),
+ }))
+
+ return (
+
+ )
+ })}
)}
diff --git a/packages/nivo-calendar/src/props.js b/packages/nivo-calendar/src/props.js
index a7b7d5851a..9de6d64d9f 100644
--- a/packages/nivo-calendar/src/props.js
+++ b/packages/nivo-calendar/src/props.js
@@ -8,6 +8,7 @@
*/
import PropTypes from 'prop-types'
import { noop } from '@nivo/core'
+import { LegendPropShape } from '@nivo/legends'
import { DIRECTION_HORIZONTAL, DIRECTION_VERTICAL } from './constants'
/**
@@ -52,6 +53,13 @@ export const CalendarPropTypes = {
isInteractive: PropTypes.bool,
onClick: PropTypes.func.isRequired,
tooltipFormat: PropTypes.oneOfType([PropTypes.func, PropTypes.string]),
+
+ legends: PropTypes.arrayOf(
+ PropTypes.shape({
+ ...LegendPropShape,
+ itemCount: PropTypes.number,
+ })
+ ).isRequired,
}
/**
@@ -84,4 +92,6 @@ export const CalendarDefaultProps = {
// interactivity
isInteractive: true,
onClick: noop,
+
+ legends: [],
}
diff --git a/packages/nivo-heatmap/src/HeatMap.js b/packages/nivo-heatmap/src/HeatMap.js
index cb269e9e63..08183973ec 100644
--- a/packages/nivo-heatmap/src/HeatMap.js
+++ b/packages/nivo-heatmap/src/HeatMap.js
@@ -10,15 +10,18 @@ import React, { Component } from 'react'
import { partial } from 'lodash'
import { TransitionMotion } from 'react-motion'
import { colorMotionSpring, getInterpolatedColor } from '@nivo/core'
+import { Container, SvgWrapper } from '@nivo/core'
+import { Grid, Axes } from '@nivo/core'
+import setDisplayName from 'recompose/setDisplayName'
import { HeatMapPropTypes } from './props'
import computeNodes from './computeNodes'
import enhance from './enhance'
-import { Container, SvgWrapper } from '@nivo/core'
-import { Grid, Axes } from '@nivo/core'
import HeatMapCellRect from './HeatMapCellRect'
import HeatMapCellCircle from './HeatMapCellCircle'
import HeatMapCellTooltip from './HeatMapCellTooltip'
+import { scaleLinear } from 'd3-scale'
+
class HeatMap extends Component {
static propTypes = HeatMapPropTypes
@@ -39,6 +42,8 @@ class HeatMap extends Component {
yScale,
offsetX,
offsetY,
+ minValue,
+ maxValue,
margin,
width,
@@ -65,6 +70,7 @@ class HeatMap extends Component {
// theming
theme,
+ colorScale,
// motion
animate,
@@ -93,6 +99,14 @@ class HeatMap extends Component {
motionStiffness,
}
+ const legendItems = scaleLinear()
+ .domain([minValue, maxValue])
+ .ticks(4)
+ .map(i => ({
+ label: i,
+ fill: colorScale(i),
+ }))
+
return (
{({ showTooltip, hideTooltip }) => {
@@ -210,4 +224,4 @@ class HeatMap extends Component {
}
}
-export default enhance(HeatMap)
+export default setDisplayName('HeatMap')(enhance(HeatMap))
diff --git a/packages/nivo-legends/package.json b/packages/nivo-legends/package.json
index 25e9b81097..4f42278d11 100644
--- a/packages/nivo-legends/package.json
+++ b/packages/nivo-legends/package.json
@@ -1,5 +1,6 @@
{
"name": "@nivo/legends",
+ "description": "legend components for nivo dataviz library",
"version": "0.32.0",
"license": "MIT",
"main": "./lib/index.js",
diff --git a/packages/nivo-legends/src/compute.js b/packages/nivo-legends/src/compute.js
new file mode 100644
index 0000000000..57875134af
--- /dev/null
+++ b/packages/nivo-legends/src/compute.js
@@ -0,0 +1,56 @@
+/*
+ * 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 { isNumber, isPlainObject } from 'lodash'
+import { DIRECTION_COLUMN, DIRECTION_ROW } from './constants'
+
+const zeroPadding = {
+ top: 0,
+ right: 0,
+ bottom: 0,
+ left: 0,
+}
+
+export const computeDimensions = ({
+ itemCount,
+ itemWidth,
+ itemHeight,
+ direction,
+ itemsSpacing,
+ padding: _padding,
+}) => {
+ let padding
+ if (isNumber(_padding)) {
+ padding = {
+ top: _padding,
+ right: _padding,
+ bottom: _padding,
+ left: _padding,
+ }
+ } else if (isPlainObject(_padding)) {
+ padding = {
+ ...zeroPadding,
+ ..._padding,
+ }
+ } else {
+ throw new TypeError(`Invalid property padding, must be one of: number, object`)
+ }
+
+ const horizontalPadding = padding.left + padding.right
+ const verticalPadding = padding.top + padding.bottom
+ let width = itemWidth + horizontalPadding
+ let height = itemHeight + verticalPadding
+ let spacing = (itemCount - 1) * itemsSpacing
+ if (direction === DIRECTION_ROW) {
+ width = itemWidth * itemCount + spacing + horizontalPadding
+ } else if (direction === DIRECTION_COLUMN) {
+ height = itemHeight * itemCount + spacing + verticalPadding
+ }
+
+ return { width, height, padding }
+}
diff --git a/packages/nivo-legends/src/constants.js b/packages/nivo-legends/src/constants.js
index 37a87c03be..1336fec985 100644
--- a/packages/nivo-legends/src/constants.js
+++ b/packages/nivo-legends/src/constants.js
@@ -17,6 +17,7 @@ export const ANCHOR_BOTTOM = 'bottom'
export const ANCHOR_BOTTOM_LEFT = 'bottom-left'
export const ANCHOR_LEFT = 'left'
export const ANCHOR_TOP_LEFT = 'top-left'
+export const ANCHOR_CENTER = 'center'
export const DIRECTION_LEFT_TO_RIGHT = 'left-to-right'
export const DIRECTION_RIGHT_TO_LEFT = 'right-to-left'
diff --git a/packages/nivo-legends/src/index.js b/packages/nivo-legends/src/index.js
index b59a4003e3..a8858f2702 100644
--- a/packages/nivo-legends/src/index.js
+++ b/packages/nivo-legends/src/index.js
@@ -7,3 +7,5 @@
* file that was distributed with this source code.
*/
export * from './svg'
+export * from './constants'
+export * from './props'
diff --git a/packages/nivo-legends/src/props.js b/packages/nivo-legends/src/props.js
new file mode 100644
index 0000000000..bbb00140e8
--- /dev/null
+++ b/packages/nivo-legends/src/props.js
@@ -0,0 +1,79 @@
+/*
+ * 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 {
+ ANCHOR_BOTTOM,
+ ANCHOR_BOTTOM_LEFT,
+ ANCHOR_BOTTOM_RIGHT,
+ ANCHOR_CENTER,
+ ANCHOR_LEFT,
+ ANCHOR_RIGHT,
+ ANCHOR_TOP,
+ ANCHOR_TOP_LEFT,
+ ANCHOR_TOP_RIGHT,
+ DIRECTION_BOTTOM_TO_TOP,
+ DIRECTION_COLUMN,
+ DIRECTION_LEFT_TO_RIGHT,
+ DIRECTION_RIGHT_TO_LEFT,
+ DIRECTION_ROW,
+ DIRECTION_TOP_TO_BOTTOM,
+} from './constants'
+
+/**
+ * The prop type is exported as a simple object instead of `PropTypes.shape`
+ * to be able to add extra properties.
+ *
+ * @example
+ * ```javascript
+ * import { LegendPropShape } from '@nivo/legends'
+ *
+ * const customLegendPropType = PropTypes.shape({
+ * ...LegendPropShape,
+ * extra: PropTypes.any.isRequired,
+ * })
+ * ```
+ */
+export const LegendPropShape = {
+ data: PropTypes.arrayOf(
+ PropTypes.shape({
+ label: PropTypes.oneOfType([PropTypes.string, PropTypes.number]).isRequired,
+ fill: PropTypes.string.isRequired,
+ })
+ ),
+
+ // position & layout
+ anchor: PropTypes.oneOf([
+ ANCHOR_TOP,
+ ANCHOR_TOP_RIGHT,
+ ANCHOR_RIGHT,
+ ANCHOR_BOTTOM_RIGHT,
+ ANCHOR_BOTTOM,
+ ANCHOR_BOTTOM_LEFT,
+ ANCHOR_LEFT,
+ ANCHOR_TOP_LEFT,
+ ANCHOR_CENTER,
+ ]).isRequired,
+ translateX: PropTypes.number,
+ translateY: PropTypes.number,
+ direction: PropTypes.oneOf([DIRECTION_ROW, DIRECTION_COLUMN]).isRequired,
+
+ // items
+ itemWidth: PropTypes.number.isRequired,
+ itemHeight: PropTypes.number.isRequired,
+ itemDirection: PropTypes.oneOf([
+ DIRECTION_LEFT_TO_RIGHT,
+ DIRECTION_RIGHT_TO_LEFT,
+ DIRECTION_TOP_TO_BOTTOM,
+ DIRECTION_BOTTOM_TO_TOP,
+ ]),
+ itemsSpacing: PropTypes.number,
+ symbolSize: PropTypes.number,
+ symbolSpacing: PropTypes.number,
+ symbolShape: PropTypes.oneOfType([PropTypes.string, PropTypes.func]),
+}
diff --git a/packages/nivo-legends/src/svg/BoxLegendSvg.js b/packages/nivo-legends/src/svg/BoxLegendSvg.js
index ae71a012c0..e99650e011 100644
--- a/packages/nivo-legends/src/svg/BoxLegendSvg.js
+++ b/packages/nivo-legends/src/svg/BoxLegendSvg.js
@@ -9,6 +9,7 @@
import React from 'react'
import PropTypes from 'prop-types'
import LegendSvg from './LegendSvg'
+import { computeDimensions } from '../compute'
import {
DIRECTION_ROW,
DIRECTION_COLUMN,
@@ -24,6 +25,7 @@ import {
ANCHOR_BOTTOM_LEFT,
ANCHOR_LEFT,
ANCHOR_TOP_LEFT,
+ ANCHOR_CENTER,
} from '../constants'
const BoxLegendSvg = ({
@@ -31,8 +33,11 @@ const BoxLegendSvg = ({
containerWidth,
containerHeight,
+ translateX,
+ translateY,
anchor,
direction,
+ padding,
justify,
itemWidth,
@@ -41,48 +46,54 @@ const BoxLegendSvg = ({
itemsSpacing,
symbolSize,
symbolSpacing,
+ symbolShape,
}) => {
- let x = 0
- let y = 0
-
- let width = itemWidth
- let height = itemHeight
- if (direction === DIRECTION_ROW) {
- width = itemWidth * data.length
- } else if (direction === DIRECTION_COLUMN) {
- height = itemHeight * data.length
- }
-
+ const { width, height } = computeDimensions({
+ itemCount: data.length,
+ itemWidth,
+ itemHeight,
+ itemsSpacing,
+ direction,
+ padding,
+ })
+
+ let x = translateX
+ let y = translateY
switch (anchor) {
case ANCHOR_TOP:
- x = (containerWidth - width) / 2
+ x += (containerWidth - width) / 2
break
case ANCHOR_TOP_RIGHT:
- x = containerWidth - width
+ x += containerWidth - width
break
case ANCHOR_RIGHT:
- x = containerWidth - width
- y = (containerHeight - height) / 2
+ x += containerWidth - width
+ y += (containerHeight - height) / 2
break
case ANCHOR_BOTTOM_RIGHT:
- x = containerWidth - width
- y = containerHeight - height
+ x += containerWidth - width
+ y += containerHeight - height
break
case ANCHOR_BOTTOM:
- x = (containerWidth - width) / 2
- y = containerHeight - height
+ x += (containerWidth - width) / 2
+ y += containerHeight - height
break
case ANCHOR_BOTTOM_LEFT:
- y = containerHeight - height
+ y += containerHeight - height
break
case ANCHOR_LEFT:
- y = (containerHeight - height) / 2
+ y += (containerHeight - height) / 2
+ break
+
+ case ANCHOR_CENTER:
+ x += (containerWidth - width) / 2
+ y += (containerHeight - height) / 2
break
}
@@ -91,9 +102,8 @@ const BoxLegendSvg = ({
data={data}
x={x}
y={y}
- width={width}
- height={height}
direction={direction}
+ padding={padding}
justify={justify}
itemWidth={itemWidth}
itemHeight={itemHeight}
@@ -101,6 +111,7 @@ const BoxLegendSvg = ({
itemsSpacing={itemsSpacing}
symbolSize={symbolSize}
symbolSpacing={symbolSpacing}
+ symbolShape={symbolShape}
/>
)
}
@@ -108,6 +119,8 @@ const BoxLegendSvg = ({
BoxLegendSvg.propTypes = {
containerWidth: PropTypes.number.isRequired,
containerHeight: PropTypes.number.isRequired,
+ translateX: PropTypes.number.isRequired,
+ translateY: PropTypes.number.isRequired,
anchor: PropTypes.oneOf([
ANCHOR_TOP,
ANCHOR_TOP_RIGHT,
@@ -117,8 +130,18 @@ BoxLegendSvg.propTypes = {
ANCHOR_BOTTOM_LEFT,
ANCHOR_LEFT,
ANCHOR_TOP_LEFT,
+ ANCHOR_CENTER,
]).isRequired,
direction: PropTypes.oneOf([DIRECTION_ROW, DIRECTION_COLUMN]).isRequired,
+ padding: PropTypes.oneOfType([
+ PropTypes.number,
+ PropTypes.shape({
+ top: PropTypes.number,
+ right: PropTypes.number,
+ bottom: PropTypes.number,
+ left: PropTypes.number,
+ }),
+ ]).isRequired,
justify: PropTypes.bool,
itemWidth: PropTypes.number.isRequired,
@@ -132,10 +155,14 @@ BoxLegendSvg.propTypes = {
itemsSpacing: PropTypes.number.isRequired,
symbolSize: PropTypes.number,
symbolSpacing: PropTypes.number,
+ symbolShape: PropTypes.oneOfType([PropTypes.string, PropTypes.func]),
}
BoxLegendSvg.defaultProps = {
- itemsSpacing: 0,
+ translateX: 0,
+ translateY: 0,
+ itemsSpacing: LegendSvg.defaultProps.itemsSpacing,
+ padding: LegendSvg.defaultProps.padding,
}
export default BoxLegendSvg
diff --git a/packages/nivo-legends/src/svg/LegendSvg.js b/packages/nivo-legends/src/svg/LegendSvg.js
index 5b725d40ae..404275d154 100644
--- a/packages/nivo-legends/src/svg/LegendSvg.js
+++ b/packages/nivo-legends/src/svg/LegendSvg.js
@@ -9,6 +9,7 @@
import React from 'react'
import PropTypes from 'prop-types'
import LegendSvgItem from './LegendSvgItem'
+import { computeDimensions } from '../compute'
import {
DIRECTION_COLUMN,
DIRECTION_ROW,
@@ -24,9 +25,8 @@ const LegendSvg = ({
// position/layout
x,
y,
- width,
- height,
direction,
+ padding: _padding,
justify,
// items
@@ -36,30 +36,44 @@ const LegendSvg = ({
itemsSpacing,
symbolSize,
symbolSpacing,
+ symbolShape,
}) => {
+ const { width, height, padding } = computeDimensions({
+ itemCount: data.length,
+ itemWidth,
+ itemHeight,
+ itemsSpacing,
+ direction,
+ padding: _padding,
+ })
+
let xStep = 0
let yStep = 0
if (direction === DIRECTION_ROW) {
- xStep = itemWidth
+ xStep = itemWidth + itemsSpacing
} else if (direction === DIRECTION_COLUMN) {
- yStep = itemHeight
+ yStep = itemHeight + itemsSpacing
}
return (
+ {/*
- {data.map((d, i) => (
+ */}
+ {data.map(({ label, fill }, i) => (
))}
@@ -67,14 +81,26 @@ const LegendSvg = ({
}
LegendSvg.propTypes = {
- data: PropTypes.array.isRequired,
+ data: PropTypes.arrayOf(
+ PropTypes.shape({
+ label: PropTypes.oneOfType([PropTypes.string, PropTypes.number]).isRequired,
+ fill: PropTypes.string.isRequired,
+ })
+ ).isRequired,
// position/layout
x: PropTypes.number.isRequired,
y: PropTypes.number.isRequired,
- width: PropTypes.number.isRequired,
- height: PropTypes.number.isRequired,
direction: PropTypes.oneOf([DIRECTION_COLUMN, DIRECTION_ROW]).isRequired,
+ padding: PropTypes.oneOfType([
+ PropTypes.number,
+ PropTypes.shape({
+ top: PropTypes.number,
+ right: PropTypes.number,
+ bottom: PropTypes.number,
+ left: PropTypes.number,
+ }),
+ ]).isRequired,
justify: PropTypes.bool.isRequired,
// items
@@ -89,11 +115,12 @@ LegendSvg.propTypes = {
itemsSpacing: PropTypes.number.isRequired,
symbolSize: PropTypes.number,
symbolSpacing: PropTypes.number,
+ symbolShape: PropTypes.oneOfType([PropTypes.string, PropTypes.func]),
}
LegendSvg.defaultProps = {
// position/layout
- direction: DIRECTION_COLUMN,
+ padding: 0,
justify: false,
// items
diff --git a/packages/nivo-legends/src/svg/LegendSvgItem.js b/packages/nivo-legends/src/svg/LegendSvgItem.js
index da46c5812c..63bad8a7b6 100644
--- a/packages/nivo-legends/src/svg/LegendSvgItem.js
+++ b/packages/nivo-legends/src/svg/LegendSvgItem.js
@@ -14,36 +14,16 @@ import {
DIRECTION_TOP_TO_BOTTOM,
DIRECTION_BOTTOM_TO_TOP,
} from '../constants'
+import { SymbolCircle, SymbolDiamond, SymbolSquare, SymbolTriangle } from './symbols'
-class LegendSvgItem extends Component {
- static propTypes = {
- x: PropTypes.number.isRequired,
- y: PropTypes.number.isRequired,
- width: PropTypes.number.isRequired,
- height: PropTypes.number.isRequired,
-
- symbolSize: PropTypes.number.isRequired,
- symbolSpacing: PropTypes.number.isRequired,
-
- label: PropTypes.oneOfType([PropTypes.string, PropTypes.number]).isRequired,
- fill: PropTypes.string.isRequired,
-
- direction: PropTypes.oneOf([
- DIRECTION_LEFT_TO_RIGHT,
- DIRECTION_RIGHT_TO_LEFT,
- DIRECTION_TOP_TO_BOTTOM,
- DIRECTION_BOTTOM_TO_TOP,
- ]).isRequired,
- justify: PropTypes.bool.isRequired,
- }
-
- static defaultProps = {
- direction: DIRECTION_LEFT_TO_RIGHT,
- justify: false,
- symbolSize: 16,
- symbolSpacing: 6,
- }
+const symbolByShape = {
+ circle: SymbolCircle,
+ diamond: SymbolDiamond,
+ square: SymbolSquare,
+ triangle: SymbolTriangle,
+}
+class LegendSvgItem extends Component {
render() {
const {
x,
@@ -52,6 +32,7 @@ class LegendSvgItem extends Component {
height,
symbolSize,
symbolSpacing,
+ symbolShape,
label,
fill,
direction,
@@ -129,15 +110,11 @@ class LegendSvgItem extends Component {
break
}
+ const Symbol = symbolByShape[symbolShape]
+
return (
-
+
)
}
+
+ static propTypes = {
+ x: PropTypes.number.isRequired,
+ y: PropTypes.number.isRequired,
+ width: PropTypes.number.isRequired,
+ height: PropTypes.number.isRequired,
+
+ symbolSize: PropTypes.number.isRequired,
+ symbolSpacing: PropTypes.number.isRequired,
+ symbolShape: PropTypes.oneOfType([
+ PropTypes.oneOf(Object.keys(symbolByShape)),
+ PropTypes.func,
+ ]).isRequired,
+
+ label: PropTypes.oneOfType([PropTypes.string, PropTypes.number]).isRequired,
+ fill: PropTypes.string.isRequired,
+
+ direction: PropTypes.oneOf([
+ DIRECTION_LEFT_TO_RIGHT,
+ DIRECTION_RIGHT_TO_LEFT,
+ DIRECTION_TOP_TO_BOTTOM,
+ DIRECTION_BOTTOM_TO_TOP,
+ ]).isRequired,
+ justify: PropTypes.bool.isRequired,
+ }
+
+ static defaultProps = {
+ direction: DIRECTION_LEFT_TO_RIGHT,
+ justify: false,
+ symbolSize: 16,
+ symbolSpacing: 8,
+ symbolShape: 'square',
+ }
}
export default LegendSvgItem
diff --git a/packages/nivo-legends/src/svg/index.js b/packages/nivo-legends/src/svg/index.js
index 743c9432b7..b1f7890949 100644
--- a/packages/nivo-legends/src/svg/index.js
+++ b/packages/nivo-legends/src/svg/index.js
@@ -8,3 +8,4 @@
*/
export { default as BoxLegendSvg } from './BoxLegendSvg'
export { default as LegendSvg } from './LegendSvg'
+export { default as LegendSvgItem } from './LegendSvgItem'
diff --git a/packages/nivo-legends/src/svg/symbols/SymbolCircle.js b/packages/nivo-legends/src/svg/symbols/SymbolCircle.js
new file mode 100644
index 0000000000..c639489718
--- /dev/null
+++ b/packages/nivo-legends/src/svg/symbols/SymbolCircle.js
@@ -0,0 +1,23 @@
+/*
+ * 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 React from 'react'
+import PropTypes from 'prop-types'
+
+const SymbolCircle = ({ x, y, size, fill }) => (
+
+)
+
+SymbolCircle.propTypes = {
+ x: PropTypes.number.isRequired,
+ y: PropTypes.number.isRequired,
+ size: PropTypes.number.isRequired,
+ fill: PropTypes.string.isRequired,
+}
+
+export default SymbolCircle
diff --git a/packages/nivo-legends/src/svg/symbols/SymbolDiamond.js b/packages/nivo-legends/src/svg/symbols/SymbolDiamond.js
new file mode 100644
index 0000000000..6df4fa5190
--- /dev/null
+++ b/packages/nivo-legends/src/svg/symbols/SymbolDiamond.js
@@ -0,0 +1,36 @@
+/*
+ * 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 React from 'react'
+import PropTypes from 'prop-types'
+
+const SymbolDiamond = ({ x, y, size, fill }) => {
+ return (
+
+
+
+ )
+}
+
+SymbolDiamond.propTypes = {
+ x: PropTypes.number.isRequired,
+ y: PropTypes.number.isRequired,
+ size: PropTypes.number.isRequired,
+ fill: PropTypes.string.isRequired,
+}
+
+export default SymbolDiamond
diff --git a/packages/nivo-legends/src/svg/symbols/SymbolSquare.js b/packages/nivo-legends/src/svg/symbols/SymbolSquare.js
new file mode 100644
index 0000000000..3770cfc389
--- /dev/null
+++ b/packages/nivo-legends/src/svg/symbols/SymbolSquare.js
@@ -0,0 +1,23 @@
+/*
+ * 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 React from 'react'
+import PropTypes from 'prop-types'
+
+const SymbolSquare = ({ x, y, size, fill }) => (
+
+)
+
+SymbolSquare.propTypes = {
+ x: PropTypes.number.isRequired,
+ y: PropTypes.number.isRequired,
+ size: PropTypes.number.isRequired,
+ fill: PropTypes.string.isRequired,
+}
+
+export default SymbolSquare
diff --git a/packages/nivo-legends/src/svg/symbols/SymbolTriangle.js b/packages/nivo-legends/src/svg/symbols/SymbolTriangle.js
new file mode 100644
index 0000000000..5afea798f0
--- /dev/null
+++ b/packages/nivo-legends/src/svg/symbols/SymbolTriangle.js
@@ -0,0 +1,33 @@
+/*
+ * 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 React from 'react'
+import PropTypes from 'prop-types'
+
+const SymbolTriangle = ({ x, y, size, fill }) => (
+
+
+
+)
+
+SymbolTriangle.propTypes = {
+ x: PropTypes.number.isRequired,
+ y: PropTypes.number.isRequired,
+ size: PropTypes.number.isRequired,
+ fill: PropTypes.string.isRequired,
+}
+
+export default SymbolTriangle
diff --git a/packages/nivo-legends/src/svg/symbols/index.js b/packages/nivo-legends/src/svg/symbols/index.js
new file mode 100644
index 0000000000..4d252744a2
--- /dev/null
+++ b/packages/nivo-legends/src/svg/symbols/index.js
@@ -0,0 +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.
+ */
+export { default as SymbolCircle } from './SymbolCircle'
+export { default as SymbolDiamond } from './SymbolDiamond'
+export { default as SymbolSquare } from './SymbolSquare'
+export { default as SymbolTriangle } from './SymbolTriangle'
diff --git a/packages/nivo-legends/tests/svg/__snapshots__/LegendSvgItem.test.js.snap b/packages/nivo-legends/tests/svg/__snapshots__/LegendSvgItem.test.js.snap
index e8b0648191..3735de1102 100644
--- a/packages/nivo-legends/tests/svg/__snapshots__/LegendSvgItem.test.js.snap
+++ b/packages/nivo-legends/tests/svg/__snapshots__/LegendSvgItem.test.js.snap
@@ -5,7 +5,7 @@ exports[`should support bottom-to-top direction 1`] = `
transform="translate(0,0)"
>
testing
@@ -32,7 +32,7 @@ exports[`should support bottom-to-top direction justified 1`] = `
transform="translate(0,0)"
>
testing
@@ -86,7 +86,7 @@ exports[`should support left-to-right direction justified 1`] = `
transform="translate(0,0)"
>
testing
@@ -140,7 +140,7 @@ exports[`should support right-to-left direction justified 1`] = `
transform="translate(0,0)"
>
testing
@@ -194,7 +194,7 @@ exports[`should support top-to-bottom direction justified 1`] = `
transform="translate(0,0)"
>
.
+
+ See the dedicated guide on how to setup
+ legends for this component.
+
)
diff --git a/website/src/components/charts/calendar/Calendar.js b/website/src/components/charts/calendar/Calendar.js
index 58c6562a9a..13088ce6bf 100644
--- a/website/src/components/charts/calendar/Calendar.js
+++ b/website/src/components/charts/calendar/Calendar.js
@@ -7,6 +7,7 @@
* file that was distributed with this source code.
*/
import React, { Component } from 'react'
+import { Link } from 'react-router-dom'
import MediaQuery from 'react-responsive'
import ChartHeader from '../../ChartHeader'
import ChartTabs from '../../ChartTabs'
@@ -30,7 +31,7 @@ export default class Calendar extends Component {
margin: {
top: 100,
right: 30,
- bottom: 30,
+ bottom: 60,
left: 30,
},
direction: 'horizontal',
@@ -51,6 +52,18 @@ export default class Calendar extends Component {
// interactivity
isInteractive: true,
+
+ legends: [
+ {
+ anchor: 'bottom-right',
+ direction: 'row',
+ translateY: 36,
+ itemCount: 4,
+ itemWidth: 34,
+ itemHeight: 36,
+ itemDirection: 'top-to-bottom',
+ },
+ ],
},
}
@@ -115,6 +128,10 @@ export default class Calendar extends Component {
This component is suitable for isomorphic rendering but require to use the{' '}
Calendar
component not the ResponsiveCalendar
one.
+
+ See the dedicated guide on how to setup
+ legends for this component.
+
)
diff --git a/website/src/components/charts/heatmap/HeatMap.js b/website/src/components/charts/heatmap/HeatMap.js
index c43a822b58..87517ce827 100644
--- a/website/src/components/charts/heatmap/HeatMap.js
+++ b/website/src/components/charts/heatmap/HeatMap.js
@@ -32,7 +32,7 @@ export default class HeatMap extends Component {
margin: {
top: 100,
right: 60,
- bottom: 30,
+ bottom: 60,
left: 60,
},
@@ -190,6 +190,10 @@ export default class HeatMap extends Component {
the storybook
.
+
+ See the dedicated guide on how to setup
+ legends for this component.
+
)
diff --git a/website/src/components/guides/legends/LegendDirection.js b/website/src/components/guides/legends/LegendDirection.js
new file mode 100644
index 0000000000..f017bb53d5
--- /dev/null
+++ b/website/src/components/guides/legends/LegendDirection.js
@@ -0,0 +1,42 @@
+import React from 'react'
+import { BoxLegendSvg } from '@nivo/legends'
+
+const legendProps = {
+ containerWidth: 800,
+ containerHeight: 90,
+ itemWidth: 70,
+ itemHeight: 24,
+}
+
+export default () => (
+
+
Legend direction
+
+ Legends support two directions, using the direction
property,{' '}
+ column
or row
.
+
+
+
+
+
+
+)
diff --git a/website/src/components/guides/legends/LegendItemDirection.js b/website/src/components/guides/legends/LegendItemDirection.js
new file mode 100644
index 0000000000..9e7d494a95
--- /dev/null
+++ b/website/src/components/guides/legends/LegendItemDirection.js
@@ -0,0 +1,83 @@
+import React from 'react'
+import {
+ LegendSvgItem,
+ DIRECTION_LEFT_TO_RIGHT,
+ DIRECTION_RIGHT_TO_LEFT,
+ DIRECTION_TOP_TO_BOTTOM,
+ DIRECTION_BOTTOM_TO_TOP,
+} from '@nivo/legends'
+
+const itemDirections = [
+ DIRECTION_LEFT_TO_RIGHT,
+ DIRECTION_RIGHT_TO_LEFT,
+ DIRECTION_TOP_TO_BOTTOM,
+ DIRECTION_BOTTOM_TO_TOP,
+]
+
+const itemsProps = {
+ x: 0,
+ y: 0,
+ width: 160,
+ height: 40,
+ fill: '#dc5a32',
+}
+
+export default () => (
+
+
Legend item direction
+
+ The itemDirection
property defines how symbol and label are positioned.
+ You have 4 available directives:
+
+
+ {itemDirections.map(dir => (
+
+
+
+
+
+ ))}
+
+
+ The behavior is slightly different if you set justify
to true
{' '}
+ as the label will be positioned at the opposite of the symbol, filling up the whole
+ width/height of the legend's item.
+
+
+ {itemDirections.map(dir => (
+
+
+
+
+
+ ))}
+
+
+)
diff --git a/website/src/components/guides/legends/LegendPosition.js b/website/src/components/guides/legends/LegendPosition.js
new file mode 100644
index 0000000000..bfd553356a
--- /dev/null
+++ b/website/src/components/guides/legends/LegendPosition.js
@@ -0,0 +1,161 @@
+import React from 'react'
+import { omit } from 'lodash'
+import {
+ BoxLegendSvg,
+ ANCHOR_TOP_LEFT,
+ ANCHOR_TOP,
+ ANCHOR_TOP_RIGHT,
+ ANCHOR_RIGHT,
+ ANCHOR_BOTTOM_RIGHT,
+ ANCHOR_BOTTOM,
+ ANCHOR_BOTTOM_LEFT,
+ ANCHOR_LEFT,
+ ANCHOR_CENTER,
+} from '@nivo/legends'
+
+const anchors = [
+ ANCHOR_TOP_LEFT,
+ ANCHOR_TOP,
+ ANCHOR_TOP_RIGHT,
+ ANCHOR_RIGHT,
+ ANCHOR_BOTTOM_RIGHT,
+ ANCHOR_BOTTOM,
+ ANCHOR_BOTTOM_LEFT,
+ ANCHOR_LEFT,
+ ANCHOR_CENTER,
+]
+
+const translateExamples = [
+ {
+ anchor: ANCHOR_TOP,
+ translateX: -160,
+ translateY: -30,
+ },
+ {
+ anchor: ANCHOR_LEFT,
+ translateX: -30,
+ translateY: 50,
+ },
+ {
+ anchor: ANCHOR_RIGHT,
+ translateX: -30,
+ translateY: -60,
+ },
+ {
+ anchor: ANCHOR_BOTTOM,
+ translateX: 160,
+ translateY: 30,
+ },
+]
+
+const margin = 30
+const legendProps = {
+ containerWidth: 800 - margin * 2,
+ containerHeight: 300 - margin * 2,
+ itemWidth: 120,
+ itemHeight: 16,
+ itemsSpacing: 4,
+ direction: 'column',
+}
+
+export default () => (
+
+
Legend position
+
+ The legend can be positioned in your chart area using the anchor
property.
+ You have 9 available directives:
+
+
+
+ margin
+
+
+
+ {anchors.map(anchor => (
+
+ ))}
+
+
+
+ The legend's anchor is relative to the inner chart area (with margin applied), but
+ depending on the chart type, you'll probably want to move it outside of this area.
+ That's where translateX
& translateY
come into play, allowing
+ to adjust the legend position from its original anchor.
+
+
+
+ margin
+
+
+
+ {translateExamples.map((example, i) => (
+
+ ))}
+ {translateExamples.map((example, i) => (
+
+ ))}
+
+
+
+)
diff --git a/website/src/components/guides/legends/Legends.js b/website/src/components/guides/legends/Legends.js
new file mode 100644
index 0000000000..a64a358955
--- /dev/null
+++ b/website/src/components/guides/legends/Legends.js
@@ -0,0 +1,33 @@
+import React, { Component } from 'react'
+import Helmet from 'react-helmet'
+import LegendPosition from './LegendPosition'
+import LegendDirection from './LegendDirection'
+import LegendItemDirection from './LegendItemDirection'
+import SymbolShape from './SymbolShape'
+
+export default class Legends extends Component {
+ render() {
+ return (
+
+
+
+
+
Let's see how to add legends to your charts.
+
+ Legend components are available via the @nivo/legends
package,
+ however it's added as a dependency for most chart packages supporting them,
+ in most cases you won't have to add it as a direct dependency.
+
+
+
+
+
+
+
+ )
+ }
+}
diff --git a/website/src/components/guides/legends/SymbolShape.js b/website/src/components/guides/legends/SymbolShape.js
new file mode 100644
index 0000000000..39e496c196
--- /dev/null
+++ b/website/src/components/guides/legends/SymbolShape.js
@@ -0,0 +1,49 @@
+import React from 'react'
+import { LegendSvgItem, DIRECTION_LEFT_TO_RIGHT } from '@nivo/legends'
+
+const shapes = ['square', 'circle', 'triangle', 'diamond']
+
+const itemsProps = {
+ x: 0,
+ y: 0,
+ width: 160,
+ height: 40,
+ fill: '#dc5a32',
+}
+
+export default () => (
+
+
Symbol shape
+
+ You can customize symbols using symbolShape
property.
+
+
+ {shapes.map(shape => (
+
+
+
+
+
+ ))}
+
+
+)
diff --git a/website/src/components/pages/About.js b/website/src/components/pages/About.js
index 8b0df088f0..ef4c57a83d 100644
--- a/website/src/components/pages/About.js
+++ b/website/src/components/pages/About.js
@@ -126,7 +126,25 @@ const About = () => (
>
nivo
{' '}
- - nivo packages & website
+ - nivo{' '}
+
+ packages
+ ,{' '}
+
+ website
+ ,{' '}
+
+ storybook
+ {' '}
+ and examples
(
{' '}
- a Docker image for the nivo http api
-
-
- nivo-generators
- {' '}
- - the data generators used for nivo-website and http API samples
-