From 4929c4684fc4dd67945bc17f7a5ca34cfd4491d9 Mon Sep 17 00:00:00 2001 From: plouc Date: Fri, 7 Jan 2022 06:58:24 +0900 Subject: [PATCH] feat(heatmap): init TypeScript migration --- packages/heatmap/index.d.ts | 96 ------ packages/heatmap/package.json | 5 +- packages/heatmap/src/HeatMap.js | 166 --------- packages/heatmap/src/HeatMap.tsx | 207 +++++++++++ .../{HeatMapCanvas.js => HeatMapCanvas.tsx} | 12 +- ...MapCellCircle.js => HeatMapCellCircle.tsx} | 8 - ...HeatMapCellRect.js => HeatMapCellRect.tsx} | 8 - packages/heatmap/src/HeatMapCellTooltip.js | 35 -- packages/heatmap/src/HeatMapCells.tsx | 167 +++++++++ .../{HeatMapCells.js => HeatMapCells_old.tsx} | 8 - packages/heatmap/src/HeatMapTooltip.tsx | 14 + packages/heatmap/src/ResponsiveHeatMap.js | 18 - packages/heatmap/src/ResponsiveHeatMap.tsx | 16 + ...pCanvas.js => ResponsiveHeatMapCanvas.tsx} | 8 - .../heatmap/src/{canvas.js => canvas.tsx} | 9 - packages/heatmap/src/compute.ts | 82 +++++ packages/heatmap/src/defaults.ts | 72 ++++ packages/heatmap/src/hooks.js | 233 ------------- packages/heatmap/src/hooks.ts | 323 ++++++++++++++++++ packages/heatmap/src/index.js | 14 - packages/heatmap/src/index.ts | 7 + packages/heatmap/src/props.js | 8 - packages/heatmap/src/types.ts | 175 ++++++++++ packages/heatmap/tsconfig.json | 8 + 24 files changed, 1076 insertions(+), 623 deletions(-) delete mode 100644 packages/heatmap/index.d.ts delete mode 100644 packages/heatmap/src/HeatMap.js create mode 100644 packages/heatmap/src/HeatMap.tsx rename packages/heatmap/src/{HeatMapCanvas.js => HeatMapCanvas.tsx} (93%) rename packages/heatmap/src/{HeatMapCellCircle.js => HeatMapCellCircle.tsx} (92%) rename packages/heatmap/src/{HeatMapCellRect.js => HeatMapCellRect.tsx} (92%) delete mode 100644 packages/heatmap/src/HeatMapCellTooltip.js create mode 100644 packages/heatmap/src/HeatMapCells.tsx rename packages/heatmap/src/{HeatMapCells.js => HeatMapCells_old.tsx} (81%) create mode 100644 packages/heatmap/src/HeatMapTooltip.tsx delete mode 100644 packages/heatmap/src/ResponsiveHeatMap.js create mode 100644 packages/heatmap/src/ResponsiveHeatMap.tsx rename packages/heatmap/src/{ResponsiveHeatMapCanvas.js => ResponsiveHeatMapCanvas.tsx} (58%) rename packages/heatmap/src/{canvas.js => canvas.tsx} (88%) create mode 100644 packages/heatmap/src/compute.ts create mode 100644 packages/heatmap/src/defaults.ts delete mode 100644 packages/heatmap/src/hooks.js create mode 100644 packages/heatmap/src/hooks.ts delete mode 100644 packages/heatmap/src/index.js create mode 100644 packages/heatmap/src/index.ts create mode 100644 packages/heatmap/src/types.ts create mode 100644 packages/heatmap/tsconfig.json diff --git a/packages/heatmap/index.d.ts b/packages/heatmap/index.d.ts deleted file mode 100644 index 0036884e60..0000000000 --- a/packages/heatmap/index.d.ts +++ /dev/null @@ -1,96 +0,0 @@ -import * as React from 'react' -import { Dimensions, Box, MotionProps, ColorProps, Theme } from '@nivo/core' -import { AxisProps } from '@nivo/axes' -import { InheritedColorConfig } from '@nivo/colors' -import { BasicTooltipProps } from '@nivo/tooltip' - -declare module '@nivo/heatmap' { - export interface HeatMapDatum { - [key: string]: string | number - } - - export type HeatMapDatumWithColor = HeatMapDatum & { - color: string - } - - export type IndexByFunc = (datum: HeatMapDatum) => string | number - - export type LabelFormatter = (datum: HeatMapDatum, key: string) => string | number - - export type ValueFormatter = (value: number) => string | number - - export interface HeatMapData { - data: HeatMapDatum[] - indexBy?: string | IndexByFunc - keys?: string[] - minValue?: number | 'auto' - maxValue?: number | 'auto' - } - - export type HeatMapCommonProps = ColorProps & - Partial<{ - forceSquare: boolean - sizeVariation: number - margin: Box - padding: number - - cellShape: 'rect' | 'circle' | React.StatelessComponent - cellOpacity: number - cellBorderWidth: number - cellBorderColor: InheritedColorConfig - - axisTop: AxisProps | null - axisRight: AxisProps | null - axisBottom: AxisProps | null - axisLeft: AxisProps | null - - enableGridX: boolean - enableGridY: boolean - - enableLabels: boolean - label: LabelFormatter - labelTextColor: InheritedColorConfig - - isInteractive: boolean - hoverTarget: 'cell' | 'row' | 'column' | 'rowColumn' - cellHoverOpacity: number - cellHoverOthersOpacity: number - tooltipFormat: string | ValueFormatter - tooltip: React.StatelessComponent - - theme: Theme - }> - - export interface NodeData { - key: string - value: number - x: number - xKey: string | number - y: number - yKey: string | number - width: number - height: number - opacity: number - } - - export type HeatMapSvgProps = HeatMapData & - HeatMapCommonProps & - MotionProps & - Partial<{ - onClick: (datum: NodeData, event: React.MouseEvent) => void - }> - - export class HeatMap extends React.Component {} - export class ResponsiveHeatMap extends React.Component {} - - export type HeatMapCanvasProps = HeatMapData & - HeatMapCommonProps & - Partial<{ - onClick: (datum: NodeData, event: React.MouseEvent) => void - pixelRatio: number - role: string - }> - - export class HeatMapCanvas extends React.Component {} - export class ResponsiveHeatMapCanvas extends React.Component {} -} diff --git a/packages/heatmap/package.json b/packages/heatmap/package.json index 166924179b..831fff9796 100644 --- a/packages/heatmap/package.json +++ b/packages/heatmap/package.json @@ -21,11 +21,12 @@ ], "main": "./dist/nivo-heatmap.cjs.js", "module": "./dist/nivo-heatmap.es.js", + "typings": "./dist/types/index.d.ts", "files": [ "README.md", "LICENSE.md", - "index.d.ts", - "dist/" + "dist/", + "!dist/tsconfig.tsbuildinfo" ], "dependencies": { "@nivo/axes": "0.78.0", diff --git a/packages/heatmap/src/HeatMap.js b/packages/heatmap/src/HeatMap.js deleted file mode 100644 index bffff61db0..0000000000 --- a/packages/heatmap/src/HeatMap.js +++ /dev/null @@ -1,166 +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 { useCallback } from 'react' -import { SvgWrapper, withContainer, useDimensions } from '@nivo/core' -import { Axes, Grid } from '@nivo/axes' -import { useTooltip } from '@nivo/tooltip' -import { HeatMapSvgPropTypes, HeatMapSvgDefaultProps } from './props' -import { useHeatMap } from './hooks' -import HeatMapCells from './HeatMapCells' -import HeatMapCellRect from './HeatMapCellRect' -import HeatMapCellCircle from './HeatMapCellCircle' -import HeatMapCellTooltip from './HeatMapCellTooltip' - -const HeatMap = ({ - data, - keys, - indexBy, - minValue, - maxValue, - width, - height, - margin: partialMargin, - forceSquare, - padding, - sizeVariation, - cellShape, - cellOpacity, - cellBorderWidth, - cellBorderColor, - axisTop, - axisRight, - axisBottom, - axisLeft, - enableGridX, - enableGridY, - enableLabels, - label, - labelTextColor, - colors, - nanColor, - isInteractive, - onClick, - hoverTarget, - cellHoverOpacity, - cellHoverOthersOpacity, - tooltipFormat, - tooltip, - role, -}) => { - const { margin, innerWidth, innerHeight, outerWidth, outerHeight } = useDimensions( - width, - height, - partialMargin - ) - - const { - cells, - xScale, - yScale, - offsetX, - offsetY, - setCurrentCellId, - getCellBorderColor, - getLabelTextColor, - } = useHeatMap({ - data, - keys, - indexBy, - minValue, - maxValue, - width: innerWidth, - height: innerHeight, - padding, - forceSquare, - sizeVariation, - colors, - nanColor, - cellOpacity, - cellBorderColor, - label, - labelTextColor, - hoverTarget, - cellHoverOpacity, - cellHoverOthersOpacity, - }) - - const { showTooltipFromEvent, hideTooltip } = useTooltip() - - const handleCellHover = useCallback( - (cell, event) => { - setCurrentCellId(cell.id) - showTooltipFromEvent( - , - event - ) - }, - [setCurrentCellId, showTooltipFromEvent, tooltipFormat, tooltip] - ) - - const handleCellLeave = useCallback(() => { - setCurrentCellId(null) - hideTooltip() - }, [setCurrentCellId, hideTooltip]) - - let cellComponent - if (cellShape === 'rect') { - cellComponent = HeatMapCellRect - } else if (cellShape === 'circle') { - cellComponent = HeatMapCellCircle - } else { - cellComponent = cellShape - } - - return ( - - - - - - ) -} - -HeatMap.propTypes = HeatMapSvgPropTypes - -const WrappedHeatMap = withContainer(HeatMap) -WrappedHeatMap.defaultProps = HeatMapSvgDefaultProps - -export default WrappedHeatMap diff --git a/packages/heatmap/src/HeatMap.tsx b/packages/heatmap/src/HeatMap.tsx new file mode 100644 index 0000000000..465379a961 --- /dev/null +++ b/packages/heatmap/src/HeatMap.tsx @@ -0,0 +1,207 @@ +import { ReactNode, Fragment, createElement } from 'react' +import { SvgWrapper, Container, useDimensions } from '@nivo/core' +import { Axes, Grid } from '@nivo/axes' +import { AnchoredContinuousColorsLegendSvg } from '@nivo/legends' +import { + DefaultHeatMapDatum, + HeatMapDatum, + HeatMapCommonProps, + HeatMapSvgProps, + LayerId, +} from './types' +import { useHeatMap } from './hooks' +import { svgDefaultProps } from './defaults' +import { HeatMapCells } from './HeatMapCells' + +type InnerHeatMapProps = Omit< + HeatMapSvgProps, + 'animate' | 'motionConfig' | 'renderWrapper' | 'theme' +> + +const InnerHeatMap = ({ + data, + layers = svgDefaultProps.layers, + minValue: _minValue = svgDefaultProps.minValue, + maxValue: _maxValue = svgDefaultProps.maxValue, + valueFormat, + width, + height, + margin: partialMargin, + forceSquare = svgDefaultProps.forceSquare, + xInnerPadding = svgDefaultProps.xInnerPadding, + xOuterPadding = svgDefaultProps.xOuterPadding, + yInnerPadding = svgDefaultProps.yInnerPadding, + yOuterPadding = svgDefaultProps.yOuterPadding, + sizeVariation = svgDefaultProps.sizeVariation, + cellComponent = svgDefaultProps.cellComponent, + opacity = svgDefaultProps.opacity, + activeOpacity = svgDefaultProps.activeOpacity, + inactiveOpacity = svgDefaultProps.inactiveOpacity, + borderRadius = svgDefaultProps.borderRadius, + borderWidth = svgDefaultProps.borderWidth, + borderColor = svgDefaultProps.borderColor as HeatMapCommonProps['borderColor'], + enableGridX = svgDefaultProps.enableGridX, + enableGridY = svgDefaultProps.enableGridY, + axisTop = svgDefaultProps.axisTop, + axisRight = svgDefaultProps.axisRight, + axisBottom = svgDefaultProps.axisBottom, + axisLeft = svgDefaultProps.axisLeft, + enableLabels = svgDefaultProps.enableLabels, + label = svgDefaultProps.label, + labelTextColor = svgDefaultProps.labelTextColor, + colors = svgDefaultProps.colors, + nanColor = svgDefaultProps.nanColor, + legends = svgDefaultProps.legends, + isInteractive = svgDefaultProps.isInteractive, + onMouseEnter, + onMouseMove, + onMouseLeave, + onClick, + hoverTarget = svgDefaultProps.hoverTarget, + tooltip = svgDefaultProps.tooltip as HeatMapCommonProps['tooltip'], + role, + ariaLabel, + ariaLabelledBy, + ariaDescribedBy, +}: InnerHeatMapProps) => { + const { margin, innerWidth, innerHeight, outerWidth, outerHeight } = useDimensions( + width, + height, + partialMargin + ) + + const { xScale, yScale, cells, colorScale } = useHeatMap({ + data, + valueFormat, + width: innerWidth, + height: innerHeight, + xInnerPadding, + xOuterPadding, + yInnerPadding, + yOuterPadding, + colors, + opacity, + activeOpacity, + inactiveOpacity, + borderColor, + label, + labelTextColor, + }) + + const layerById: Record = { + grid: null, + axes: null, + cells: null, + legends: null, + } + + if (layers.includes('grid')) { + layerById.grid = ( + + ) + } + + if (layers.includes('axes')) { + layerById.axes = ( + + ) + } + + if (layers.includes('cells')) { + layerById.cells = ( + + + cells={cells} + borderRadius={borderRadius} + borderWidth={borderWidth} + isInteractive={isInteractive} + onMouseEnter={onMouseEnter} + onMouseMove={onMouseMove} + onMouseLeave={onMouseLeave} + onClick={onClick} + tooltip={tooltip} + enableLabels={enableLabels} + /> + + ) + } + + if (layers.includes('legends')) { + layerById.legends = ( + + {legends.map((legend, index) => ( + + ))} + + ) + } + + return ( + + {layers.map((layer, i) => { + if (typeof layer === 'function') { + return {createElement(layer, {})} + } + + return layerById?.[layer] ?? null + })} + + ) +} + +export const HeatMap = < + Datum extends HeatMapDatum = DefaultHeatMapDatum, + ExtraProps extends object = Record +>({ + isInteractive = svgDefaultProps.isInteractive, + animate = svgDefaultProps.animate, + motionConfig = svgDefaultProps.motionConfig, + theme, + renderWrapper, + ...otherProps +}: HeatMapSvgProps) => ( + + isInteractive={isInteractive} {...otherProps} /> + +) diff --git a/packages/heatmap/src/HeatMapCanvas.js b/packages/heatmap/src/HeatMapCanvas.tsx similarity index 93% rename from packages/heatmap/src/HeatMapCanvas.js rename to packages/heatmap/src/HeatMapCanvas.tsx index d391087387..7c0d9eecb0 100644 --- a/packages/heatmap/src/HeatMapCanvas.js +++ b/packages/heatmap/src/HeatMapCanvas.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 { useEffect, useRef, useCallback } from 'react' import { getRelativeCursor, @@ -19,7 +11,7 @@ import { useTooltip } from '@nivo/tooltip' import { useHeatMap } from './hooks' import { HeatMapDefaultProps, HeatMapPropTypes } from './props' import { renderRect, renderCircle } from './canvas' -import HeatMapCellTooltip from './HeatMapCellTooltip' +import { HeatMapTooltip } from './HeatMapTooltip' const HeatMapCanvas = ({ data, @@ -165,7 +157,7 @@ const HeatMapCanvas = ({ if (cell !== undefined) { setCurrentCellId(cell.id) showTooltipFromEvent( - , + , event ) } else { diff --git a/packages/heatmap/src/HeatMapCellCircle.js b/packages/heatmap/src/HeatMapCellCircle.tsx similarity index 92% rename from packages/heatmap/src/HeatMapCellCircle.js rename to packages/heatmap/src/HeatMapCellCircle.tsx index bf71e376e3..53f1331b57 100644 --- a/packages/heatmap/src/HeatMapCellCircle.js +++ b/packages/heatmap/src/HeatMapCellCircle.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 { memo } from 'react' import PropTypes from 'prop-types' import { useSpring, animated } from '@react-spring/web' diff --git a/packages/heatmap/src/HeatMapCellRect.js b/packages/heatmap/src/HeatMapCellRect.tsx similarity index 92% rename from packages/heatmap/src/HeatMapCellRect.js rename to packages/heatmap/src/HeatMapCellRect.tsx index 830adcffbd..edf9609dcd 100644 --- a/packages/heatmap/src/HeatMapCellRect.js +++ b/packages/heatmap/src/HeatMapCellRect.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 { memo } from 'react' import PropTypes from 'prop-types' import { useSpring, animated } from '@react-spring/web' diff --git a/packages/heatmap/src/HeatMapCellTooltip.js b/packages/heatmap/src/HeatMapCellTooltip.js deleted file mode 100644 index 04f9c5875b..0000000000 --- a/packages/heatmap/src/HeatMapCellTooltip.js +++ /dev/null @@ -1,35 +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 HeatMapCellTooltip = ({ cell, format, tooltip }) => ( - -) - -HeatMapCellTooltip.propTypes = { - cell: PropTypes.shape({ - xKey: PropTypes.string.isRequired, - yKey: PropTypes.string.isRequired, - value: PropTypes.number.isRequired, - color: PropTypes.string.isRequired, - }).isRequired, - format: PropTypes.func, - tooltip: PropTypes.func, -} - -export default memo(HeatMapCellTooltip) diff --git a/packages/heatmap/src/HeatMapCells.tsx b/packages/heatmap/src/HeatMapCells.tsx new file mode 100644 index 0000000000..c310344c5f --- /dev/null +++ b/packages/heatmap/src/HeatMapCells.tsx @@ -0,0 +1,167 @@ +import { createElement, MouseEvent, useCallback } from 'react' +import { useTransition, animated, to } from '@react-spring/web' +import { useTheme, useMotionConfig } from '@nivo/core' +import { useTooltip } from '@nivo/tooltip' +import { ComputedCell, HeatMapDatum, HeatMapSvgProps } from './types' + +interface HeatMapCellsProps { + cells: ComputedCell[] + borderRadius: NonNullable['borderRadius']> + borderWidth: NonNullable['borderWidth']> + onMouseEnter: HeatMapSvgProps['onMouseEnter'] + onMouseMove: HeatMapSvgProps['onMouseMove'] + onMouseLeave: HeatMapSvgProps['onMouseLeave'] + onClick: HeatMapSvgProps['onClick'] + tooltip: NonNullable['tooltip']> + isInteractive: NonNullable['isInteractive']> + enableLabels: NonNullable['enableLabels']> +} + +/* +let cellComponent +if (cellShape === 'rect') { + cellComponent = HeatMapCellRect +} else if (cellShape === 'circle') { + cellComponent = HeatMapCellCircle +} else { + cellComponent = cellShape +} +*/ + +export const HeatMapCells = ({ + cells, + borderRadius, + borderWidth, + onMouseEnter, + onMouseMove, + onMouseLeave, + onClick, + tooltip, + isInteractive, + enableLabels, +}: HeatMapCellsProps) => { + const theme = useTheme() + const { animate, config: springConfig } = useMotionConfig() + + const transition = useTransition< + ComputedCell, + { + x: number + y: number + width: number + height: number + color: string + opacity: number + borderColor: string + labelTextColor: string + } + >(cells, { + keys: cell => cell.id, + initial: cell => ({ + x: cell.x, + y: cell.y, + width: cell.width, + height: cell.height, + color: cell.color, + borderColor: cell.borderColor, + labelTextColor: cell.labelTextColor, + }), + update: cell => ({ + x: cell.x, + y: cell.y, + width: cell.width, + height: cell.height, + color: cell.color, + borderColor: cell.borderColor, + labelTextColor: cell.labelTextColor, + }), + config: springConfig, + immediate: !animate, + }) + + const { showTooltipFromEvent, hideTooltip } = useTooltip() + + const handleMouseEnter = useCallback( + (cell: ComputedCell, event: MouseEvent) => { + showTooltipFromEvent(createElement(tooltip, { cell }), event) + onMouseEnter?.(cell, event) + }, + [showTooltipFromEvent, tooltip, onMouseEnter] + ) + + const handleMouseMove = useCallback( + (cell: ComputedCell, event: MouseEvent) => { + showTooltipFromEvent(createElement(tooltip, { cell }), event) + onMouseMove?.(cell, event) + }, + [showTooltipFromEvent, tooltip, onMouseMove] + ) + + const handleMouseLeave = useCallback( + (cell: ComputedCell, event: MouseEvent) => { + hideTooltip() + onMouseLeave?.(cell, event) + }, + [hideTooltip, onMouseLeave] + ) + + return ( + + {transition((animatedProps, cell) => { + return ( + { + return `translate(${x - width / 2}, ${y - height / 2})` + } + )} + > + handleMouseEnter(cell, event) : undefined + } + onMouseMove={ + isInteractive ? event => handleMouseMove(cell, event) : undefined + } + onMouseLeave={ + isInteractive ? event => handleMouseLeave(cell, event) : undefined + } + onClick={isInteractive ? event => onClick?.(cell, event) : undefined} + /> + {enableLabels && ( + + {cell.label} + + )} + + ) + })} + + ) +} diff --git a/packages/heatmap/src/HeatMapCells.js b/packages/heatmap/src/HeatMapCells_old.tsx similarity index 81% rename from packages/heatmap/src/HeatMapCells.js rename to packages/heatmap/src/HeatMapCells_old.tsx index f74e498261..2c96ec6d0a 100644 --- a/packages/heatmap/src/HeatMapCells.js +++ b/packages/heatmap/src/HeatMapCells_old.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 } from 'react' const HeatMapCells = ({ diff --git a/packages/heatmap/src/HeatMapTooltip.tsx b/packages/heatmap/src/HeatMapTooltip.tsx new file mode 100644 index 0000000000..a42bc47199 --- /dev/null +++ b/packages/heatmap/src/HeatMapTooltip.tsx @@ -0,0 +1,14 @@ +import { memo } from 'react' +import { BasicTooltip } from '@nivo/tooltip' +import { HeatMapDatum, TooltipProps } from './types' + +const NonMemoizedHeatMapTooltip = ({ cell }: TooltipProps) => ( + +) + +export const HeatMapTooltip = memo(NonMemoizedHeatMapTooltip) as typeof NonMemoizedHeatMapTooltip diff --git a/packages/heatmap/src/ResponsiveHeatMap.js b/packages/heatmap/src/ResponsiveHeatMap.js deleted file mode 100644 index e691076015..0000000000 --- a/packages/heatmap/src/ResponsiveHeatMap.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 HeatMap from './HeatMap' - -const ResponsiveHeatMap = props => ( - - {({ width, height }) => } - -) - -export default ResponsiveHeatMap diff --git a/packages/heatmap/src/ResponsiveHeatMap.tsx b/packages/heatmap/src/ResponsiveHeatMap.tsx new file mode 100644 index 0000000000..31cb9fa0f5 --- /dev/null +++ b/packages/heatmap/src/ResponsiveHeatMap.tsx @@ -0,0 +1,16 @@ +import { ResponsiveWrapper } from '@nivo/core' +import { DefaultHeatMapDatum, HeatMapDatum, HeatMapSvgProps } from './types' +import { HeatMap } from './HeatMap' + +export const ResponsiveHeatMap = < + Datum extends HeatMapDatum = DefaultHeatMapDatum, + ExtraProps extends object = Record +>( + props: Omit, 'height' | 'width'> +) => ( + + {({ width, height }) => ( + width={width} height={height} {...props} /> + )} + +) diff --git a/packages/heatmap/src/ResponsiveHeatMapCanvas.js b/packages/heatmap/src/ResponsiveHeatMapCanvas.tsx similarity index 58% rename from packages/heatmap/src/ResponsiveHeatMapCanvas.js rename to packages/heatmap/src/ResponsiveHeatMapCanvas.tsx index f104f2f6bb..f8f7168693 100644 --- a/packages/heatmap/src/ResponsiveHeatMapCanvas.js +++ b/packages/heatmap/src/ResponsiveHeatMapCanvas.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 { ResponsiveWrapper } from '@nivo/core' import HeatMapCanvas from './HeatMapCanvas' diff --git a/packages/heatmap/src/canvas.js b/packages/heatmap/src/canvas.tsx similarity index 88% rename from packages/heatmap/src/canvas.js rename to packages/heatmap/src/canvas.tsx index 44e49ea307..f8e33c2114 100644 --- a/packages/heatmap/src/canvas.js +++ b/packages/heatmap/src/canvas.tsx @@ -1,12 +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. - */ - /** * Render heatmap rect cell. * diff --git a/packages/heatmap/src/compute.ts b/packages/heatmap/src/compute.ts new file mode 100644 index 0000000000..522e032c6d --- /dev/null +++ b/packages/heatmap/src/compute.ts @@ -0,0 +1,82 @@ +import { scaleBand } from 'd3-scale' +import { castBandScale } from '@nivo/scales' +import { ComputedCell, HeatMapCommonProps, HeatMapDataProps, HeatMapDatum } from './types' + +export const computeCells = ({ + data, + width, + height, + xInnerPadding, + xOuterPadding, + yInnerPadding, + yOuterPadding, +}: { + data: HeatMapDataProps['data'] + width: number + height: number + xInnerPadding: HeatMapCommonProps['xInnerPadding'] + xOuterPadding: HeatMapCommonProps['xOuterPadding'] + yInnerPadding: HeatMapCommonProps['yInnerPadding'] + yOuterPadding: HeatMapCommonProps['yOuterPadding'] +}) => { + const xValuesSet = new Set() + const serieIds: string[] = [] + const allValues: number[] = [] + + const cells: Pick, 'id' | 'serieId' | 'value' | 'data'>[] = [] + + data.forEach(serie => { + serieIds.push(serie.id) + + serie.data.forEach(datum => { + xValuesSet.add(datum.x) + allValues.push(datum.y) + + cells.push({ + id: `${datum.x}.${serie.id}`, + serieId: serie.id, + value: datum.y, + data: datum, + }) + }) + }) + + const xValues = Array.from(xValuesSet) + const xScale = castBandScale( + scaleBand() + .domain(xValues) + .range([0, width]) + .paddingOuter(xOuterPadding) + .paddingInner(xInnerPadding) + ) + + const yScale = castBandScale( + scaleBand() + .domain(serieIds) + .range([0, height]) + .paddingOuter(yOuterPadding) + .paddingInner(yInnerPadding) + ) + + const cellWidth = xScale.bandwidth() + const cellHeight = yScale.bandwidth() + + const cellsWithPosition: Omit< + ComputedCell, + 'formattedValue' | 'color' | 'opacity' | 'borderColor' + >[] = cells.map(cell => ({ + ...cell, + x: xScale(cell.data.x)! + cellWidth / 2, + y: yScale(cell.serieId)! + cellHeight / 2, + width: cellWidth, + height: cellHeight, + })) + + return { + xScale, + yScale, + minValue: Math.min(...allValues), + maxValue: Math.max(...allValues), + cells: cellsWithPosition, + } +} diff --git a/packages/heatmap/src/defaults.ts b/packages/heatmap/src/defaults.ts new file mode 100644 index 0000000000..ef83993e93 --- /dev/null +++ b/packages/heatmap/src/defaults.ts @@ -0,0 +1,72 @@ +import { DefaultHeatMapDatum, HeatMapCommonProps, LayerId } from './types' +import { HeatMapTooltip } from './HeatMapTooltip' + +export const commonDefaultProps: Omit< + HeatMapCommonProps, + | 'margin' + | 'theme' + | 'onClick' + | 'renderWrapper' + | 'role' + | 'ariaLabel' + | 'ariaLabelledBy' + | 'ariaDescribedBy' +> & { + layers: LayerId[] +} = { + layers: ['grid', 'axes', 'cells', 'legends'], + + minValue: 'auto', + maxValue: 'auto', + + forceSquare: false, + xInnerPadding: 0, + xOuterPadding: 0, + yInnerPadding: 0, + yOuterPadding: 0, + sizeVariation: 0, + + opacity: 0.85, + activeOpacity: 1, + inactiveOpacity: 0.35, + borderWidth: 0, + borderColor: { from: 'color' }, + + enableGridX: false, + enableGridY: false, + axisTop: {}, + axisRight: null, + axisBottom: null, + axisLeft: {}, + + enableLabels: true, + label: 'formattedValue', + labelTextColor: { from: 'color', modifiers: [['darker', 1.4]] }, + + colors: { + type: 'sequential', + scheme: 'brown_blueGreen', + }, + nanColor: '#000000', + + legends: [], + + isInteractive: true, + hoverTarget: 'rowColumn', + tooltip: HeatMapTooltip, + + animate: true, + motionConfig: 'gentle' as const, +} + +export const svgDefaultProps = { + ...commonDefaultProps, + borderRadius: 0, + cellComponent: 'rect', +} + +export const canvasDefaultProps = { + ...commonDefaultProps, + renderCell: 'rect', + pixelRatio: typeof window !== 'undefined' ? window.devicePixelRatio || 1 : 1, +} diff --git a/packages/heatmap/src/hooks.js b/packages/heatmap/src/hooks.js deleted file mode 100644 index 52b4845a49..0000000000 --- a/packages/heatmap/src/hooks.js +++ /dev/null @@ -1,233 +0,0 @@ -import { useState, useMemo } from 'react' -import { scaleOrdinal, scaleLinear } from 'd3-scale' -import { - useTheme, - usePropertyAccessor, - guessQuantizeColorScale, - getLabelGenerator, -} from '@nivo/core' -import { useInheritedColor } from '@nivo/colors' - -const computeX = (column, cellWidth, padding) => { - return column * cellWidth + cellWidth * 0.5 + padding * column + padding -} -const computeY = (row, cellHeight, padding) => { - return row * cellHeight + cellHeight * 0.5 + padding * row + padding -} - -const isHoverTargetByType = { - cell: (cell, current) => cell.xKey === current.xKey && cell.yKey === current.yKey, - row: (cell, current) => cell.yKey === current.yKey, - column: (cell, current) => cell.xKey === current.xKey, - rowColumn: (cell, current) => cell.xKey === current.xKey || cell.yKey === current.yKey, -} - -const computeCells = ({ - data, - keys, - getIndex, - xScale, - yScale, - sizeScale, - cellOpacity, - cellWidth, - cellHeight, - colorScale, - nanColor, - getLabel, - getLabelTextColor, -}) => { - const cells = [] - data.forEach(datum => { - keys.forEach(key => { - const value = datum[key] - const label = getLabel(datum, key) - const index = getIndex(datum) - const sizeMultiplier = sizeScale ? sizeScale(value) : 1 - const width = sizeMultiplier * cellWidth - const height = sizeMultiplier * cellHeight - - const cell = { - id: `${key}.${index}`, - xKey: key, - yKey: index, - x: xScale(key), - y: yScale(index), - width, - height, - value, - label, - color: isNaN(value) ? nanColor : colorScale(value), - opacity: cellOpacity, - } - cell.labelTextColor = getLabelTextColor(cell) - - cells.push(cell) - }) - }) - - return cells -} - -export const useHeatMap = ({ - data, - keys, - indexBy, - minValue: _minValue = 'auto', - maxValue: _maxValue = 'auto', - width, - height, - padding, - forceSquare, - sizeVariation, - colors, - nanColor, - cellOpacity, - cellBorderColor, - label, - labelTextColor, - hoverTarget, - cellHoverOpacity, - cellHoverOthersOpacity, -}) => { - const [currentCellId, setCurrentCellId] = useState(null) - - const getIndex = usePropertyAccessor(indexBy) - const indices = useMemo(() => data.map(getIndex), [data, getIndex]) - const getLabel = useMemo(() => getLabelGenerator(label), [label]) - - const layoutConfig = useMemo(() => { - const columns = keys.length - const rows = data.length - - let cellWidth = Math.max((width - padding * (columns + 1)) / columns, 0) - let cellHeight = Math.max((height - padding * (rows + 1)) / rows, 0) - - let offsetX = 0 - let offsetY = 0 - if (forceSquare === true) { - const cellSize = Math.min(cellWidth, cellHeight) - cellWidth = cellSize - cellHeight = cellSize - - offsetX = (width - ((cellWidth + padding) * columns + padding)) / 2 - offsetY = (height - ((cellHeight + padding) * rows + padding)) / 2 - } - - return { - cellWidth, - cellHeight, - offsetX, - offsetY, - } - }, [data, keys, width, height, padding, forceSquare]) - - const scales = useMemo(() => { - return { - x: scaleOrdinal( - keys.map((key, i) => computeX(i, layoutConfig.cellWidth, padding)) - ).domain(keys), - y: scaleOrdinal( - indices.map((d, i) => computeY(i, layoutConfig.cellHeight, padding)) - ).domain(indices), - } - }, [indices, keys, layoutConfig, padding]) - - const values = useMemo(() => { - let minValue = _minValue - let maxValue = _maxValue - if (minValue === 'auto' || maxValue === 'auto') { - const allValues = data.reduce((acc, row) => acc.concat(keys.map(key => row[key])), []) - - if (minValue === 'auto') minValue = Math.min(...allValues) - if (maxValue === 'auto') maxValue = Math.max(...allValues) - } - - return { - min: Math.min(minValue, maxValue), - max: Math.max(maxValue, minValue), - } - }, [data, keys, _minValue, _maxValue]) - - const sizeScale = useMemo(() => { - if (sizeVariation > 0) { - return scaleLinear() - .range([1 - sizeVariation, 1]) - .domain([values.min, values.max]) - } - }, [sizeVariation, values]) - - const colorScale = useMemo( - () => guessQuantizeColorScale(colors).domain([values.min, values.max]), - [colors, values] - ) - const theme = useTheme() - const getCellBorderColor = useInheritedColor(cellBorderColor, theme) - const getLabelTextColor = useInheritedColor(labelTextColor, theme) - - const cells = useMemo( - () => - computeCells({ - data, - keys, - getIndex, - xScale: scales.x, - yScale: scales.y, - sizeScale, - cellOpacity, - cellWidth: layoutConfig.cellWidth, - cellHeight: layoutConfig.cellHeight, - colorScale, - nanColor, - getLabel, - getLabelTextColor, - }), - [ - data, - keys, - getIndex, - scales, - sizeScale, - cellOpacity, - layoutConfig, - colorScale, - nanColor, - getLabel, - getLabelTextColor, - ] - ) - - const cellsWithCurrent = useMemo(() => { - if (currentCellId === null) return cells - - const isHoverTarget = isHoverTargetByType[hoverTarget] - const currentCell = cells.find(cell => cell.id === currentCellId) - - return cells.map(cell => { - const opacity = isHoverTarget(cell, currentCell) - ? cellHoverOpacity - : cellHoverOthersOpacity - - if (opacity === cell.opacity) return cell - - return { - ...cell, - opacity, - } - }) - }, [cells, currentCellId, hoverTarget, cellHoverOpacity, cellHoverOthersOpacity]) - - return { - cells: cellsWithCurrent, - getIndex, - xScale: scales.x, - yScale: scales.y, - ...layoutConfig, - sizeScale, - currentCellId, - setCurrentCellId, - colorScale, - getCellBorderColor, - getLabelTextColor, - } -} diff --git a/packages/heatmap/src/hooks.ts b/packages/heatmap/src/hooks.ts new file mode 100644 index 0000000000..a3de99ee7a --- /dev/null +++ b/packages/heatmap/src/hooks.ts @@ -0,0 +1,323 @@ +import { useMemo, useCallback } from 'react' +import { + useTheme, + usePropertyAccessor, + useValueFormatter, + // @ts-ignore + getLabelGenerator, +} from '@nivo/core' +import { useInheritedColor, useContinuousColorScale } from '@nivo/colors' +import { + ComputedCell, + DefaultHeatMapDatum, + HeatMapCommonProps, + HeatMapDataProps, + HeatMapDatum, +} from './types' +import { commonDefaultProps } from './defaults' +import { computeCells } from './compute' + +export const useComputeCells = ({ + data, + width, + height, + xInnerPadding, + xOuterPadding, + yInnerPadding, + yOuterPadding, +}: { + data: HeatMapDataProps['data'] + width: number + height: number + xInnerPadding: HeatMapCommonProps['xInnerPadding'] + xOuterPadding: HeatMapCommonProps['xOuterPadding'] + yInnerPadding: HeatMapCommonProps['yInnerPadding'] + yOuterPadding: HeatMapCommonProps['yOuterPadding'] +}) => + useMemo( + () => + computeCells({ + data, + width, + height, + xInnerPadding, + xOuterPadding, + yInnerPadding, + yOuterPadding, + }), + [data, width, height, xInnerPadding, xOuterPadding, yInnerPadding, yOuterPadding] + ) + +const computeX = (column: number, cellWidth: number, padding: number) => { + return column * cellWidth + cellWidth * 0.5 + padding * column + padding +} +const computeY = (row: number, cellHeight: number, padding: number) => { + return row * cellHeight + cellHeight * 0.5 + padding * row + padding +} + +const isHoverTargetByType = { + cell: (cell: ComputedCell, current: ComputedCell) => + cell.xKey === current.xKey && cell.yKey === current.yKey, + row: (cell: ComputedCell, current: ComputedCell) => cell.yKey === current.yKey, + column: (cell: ComputedCell, current: ComputedCell) => + cell.xKey === current.xKey, + rowColumn: (cell: ComputedCell, current: ComputedCell) => + cell.xKey === current.xKey || cell.yKey === current.yKey, +} + +/* +const computeCells = ({ + data, + keys, + getIndex, + xScale, + yScale, + sizeScale, + cellOpacity, + cellWidth, + cellHeight, + colorScale, + nanColor, + getLabel, + getLabelTextColor, +}: { + data: HeatMapDataProps['data'] + keys: HeatMapCommonProps['keys'] + getIndex: (datum: Datum) => string | number +}) => { + const cells = [] + data.forEach(datum => { + keys.forEach(key => { + const value = datum[key] + const label = getLabel(datum, key) + const index = getIndex(datum) + const sizeMultiplier = sizeScale ? sizeScale(value) : 1 + const width = sizeMultiplier * cellWidth + const height = sizeMultiplier * cellHeight + + const cell = { + id: `${key}.${index}`, + xKey: key, + yKey: index, + x: xScale(key), + y: yScale(index), + width, + height, + value, + label, + color: isNaN(value) ? nanColor : colorScale(value), + opacity: cellOpacity, + } + cell.labelTextColor = getLabelTextColor(cell) + + cells.push(cell) + }) + }) + + return cells +} +*/ + +export const useHeatMap = < + Datum extends HeatMapDatum = DefaultHeatMapDatum, + ExtraProps extends object = Record +>({ + data, + minValue: _minValue = commonDefaultProps.minValue, + maxValue: _maxValue = commonDefaultProps.maxValue, + valueFormat, + width, + height, + forceSquare = commonDefaultProps.forceSquare, + xOuterPadding = commonDefaultProps.xOuterPadding, + xInnerPadding = commonDefaultProps.xInnerPadding, + yOuterPadding = commonDefaultProps.yOuterPadding, + yInnerPadding = commonDefaultProps.yInnerPadding, + sizeVariation, + colors = commonDefaultProps.colors as HeatMapCommonProps['colors'], + nanColor, + opacity, + activeOpacity, + inactiveOpacity, + borderColor = commonDefaultProps.borderColor as HeatMapCommonProps['borderColor'], + label = commonDefaultProps.label as HeatMapCommonProps['label'], + labelTextColor = commonDefaultProps.labelTextColor as HeatMapCommonProps['labelTextColor'], + hoverTarget, +}: { + data: HeatMapDataProps['data'] + minValue?: HeatMapCommonProps['minValue'] + maxValue?: HeatMapCommonProps['maxValue'] + valueFormat?: HeatMapCommonProps['valueFormat'] + width: number + height: number + forceSquare?: HeatMapCommonProps['forceSquare'] + xOuterPadding?: HeatMapCommonProps['xOuterPadding'] + xInnerPadding?: HeatMapCommonProps['xInnerPadding'] + yOuterPadding?: HeatMapCommonProps['yOuterPadding'] + yInnerPadding?: HeatMapCommonProps['yInnerPadding'] + sizeVariation?: HeatMapCommonProps['sizeVariation'] + colors?: HeatMapCommonProps['colors'] + nanColor?: HeatMapCommonProps['nanColor'] + opacity?: HeatMapCommonProps['opacity'] + activeOpacity?: HeatMapCommonProps['activeOpacity'] + inactiveOpacity?: HeatMapCommonProps['inactiveOpacity'] + borderColor?: HeatMapCommonProps['borderColor'] + label?: HeatMapCommonProps['label'] + labelTextColor?: HeatMapCommonProps['labelTextColor'] + hoverTarget?: HeatMapCommonProps['hoverTarget'] +}) => { + const { cells, xScale, yScale, minValue, maxValue } = useComputeCells({ + data, + width, + height, + xOuterPadding, + xInnerPadding, + yOuterPadding, + yInnerPadding, + }) + + const colorScale = useContinuousColorScale(colors, { + min: minValue, + max: maxValue, + }) + + const getColor = useCallback( + (cell: Omit, 'color' | 'opacity' | 'borderColor'>) => + colorScale(cell.value), + [colorScale] + ) + const theme = useTheme() + const getBorderColor = useInheritedColor(borderColor, theme) + const getLabelTextColor = useInheritedColor(labelTextColor, theme) + const formatValue = useValueFormatter(valueFormat) + const getLabel = usePropertyAccessor(label) + + const computedCells = cells.map(cell => { + const computedCell = { + ...cell, + formattedValue: formatValue(cell.value), + opacity: 1, + } as ComputedCell + + computedCell.label = getLabel(computedCell) + computedCell.color = getColor(computedCell) + computedCell.borderColor = getBorderColor(computedCell) + computedCell.labelTextColor = getLabelTextColor(computedCell) + + return computedCell + }) + + return { + cells: computedCells, + xScale, + yScale, + colorScale, + } + + /* + const [currentCellId, setCurrentCellId] = useState(null) + + const layoutConfig = useMemo(() => { + const columns = keys.length + const rows = data.length + + let cellWidth = Math.max((width - padding * (columns + 1)) / columns, 0) + let cellHeight = Math.max((height - padding * (rows + 1)) / rows, 0) + + let offsetX = 0 + let offsetY = 0 + if (forceSquare === true) { + const cellSize = Math.min(cellWidth, cellHeight) + cellWidth = cellSize + cellHeight = cellSize + + offsetX = (width - ((cellWidth + padding) * columns + padding)) / 2 + offsetY = (height - ((cellHeight + padding) * rows + padding)) / 2 + } + + return { + cellWidth, + cellHeight, + offsetX, + offsetY, + } + }, [data, keys, width, height, padding, forceSquare]) + + const sizeScale = useMemo(() => { + if (sizeVariation > 0) { + return scaleLinear() + .range([1 - sizeVariation, 1]) + .domain([values.min, values.max]) + } + }, [sizeVariation, values]) + + const getCellBorderColor = useInheritedColor(cellBorderColor, theme) + const getLabelTextColor = useInheritedColor(labelTextColor, theme) + + const cells = useMemo( + () => + computeCells({ + data, + keys, + getIndex, + xScale: scales.x, + yScale: scales.y, + sizeScale, + cellOpacity, + cellWidth: layoutConfig.cellWidth, + cellHeight: layoutConfig.cellHeight, + colorScale, + nanColor, + getLabel, + getLabelTextColor, + }), + [ + data, + keys, + getIndex, + scales, + sizeScale, + cellOpacity, + layoutConfig, + colorScale, + nanColor, + getLabel, + getLabelTextColor, + ] + ) + + const cellsWithCurrent = useMemo(() => { + if (currentCellId === null) return cells + + const isHoverTarget = isHoverTargetByType[hoverTarget] + const currentCell = cells.find(cell => cell.id === currentCellId) + + return cells.map(cell => { + const opacity = isHoverTarget(cell, currentCell) + ? cellHoverOpacity + : cellHoverOthersOpacity + + if (opacity === cell.opacity) return cell + + return { + ...cell, + opacity, + } + }) + }, [cells, currentCellId, hoverTarget, cellHoverOpacity, cellHoverOthersOpacity]) + + return { + cells: cellsWithCurrent, + getIndex, + xScale: scales.x, + yScale: scales.y, + ...layoutConfig, + sizeScale, + currentCellId, + setCurrentCellId, + colorScale, + getCellBorderColor, + getLabelTextColor, + } + */ +} diff --git a/packages/heatmap/src/index.js b/packages/heatmap/src/index.js deleted file mode 100644 index b2ee1130cc..0000000000 --- a/packages/heatmap/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 HeatMap } from './HeatMap' -export { default as HeatMapCanvas } from './HeatMapCanvas' -export { default as ResponsiveHeatMap } from './ResponsiveHeatMap' -export { default as ResponsiveHeatMapCanvas } from './ResponsiveHeatMapCanvas' -export * from './hooks' -export * from './props' diff --git a/packages/heatmap/src/index.ts b/packages/heatmap/src/index.ts new file mode 100644 index 0000000000..49a5f147eb --- /dev/null +++ b/packages/heatmap/src/index.ts @@ -0,0 +1,7 @@ +export * from './HeatMap' +export * from './ResponsiveHeatMap' +export { default as HeatMapCanvas } from './HeatMapCanvas' +export { default as ResponsiveHeatMapCanvas } from './ResponsiveHeatMapCanvas' +export * from './hooks' +export * from './props' +export * from './defaults' diff --git a/packages/heatmap/src/props.js b/packages/heatmap/src/props.js index bbae2c9ba0..e09c6c74ca 100644 --- a/packages/heatmap/src/props.js +++ b/packages/heatmap/src/props.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 PropTypes from 'prop-types' import { quantizeColorScalePropType, noop } from '@nivo/core' import { inheritedColorPropType } from '@nivo/colors' diff --git a/packages/heatmap/src/types.ts b/packages/heatmap/src/types.ts new file mode 100644 index 0000000000..30ad884da0 --- /dev/null +++ b/packages/heatmap/src/types.ts @@ -0,0 +1,175 @@ +import { AriaAttributes, MouseEvent, FunctionComponent } from 'react' +import { AnimatedProps } from '@react-spring/web' +import { + Box, + Theme, + Dimensions, + ModernMotionProps, + PropertyAccessor, + ValueFormat, +} from '@nivo/core' +import { AxisProps } from '@nivo/axes' +import { InheritedColorConfig, ContinuousColorScaleConfig } from '@nivo/colors' +import { AnchoredContinuousColorsLegendProps } from '@nivo/legends' + +export interface HeatMapDatum { + x: string | number + y: number +} + +export interface DefaultHeatMapDatum { + x: string + y: number +} + +export type HeatMapSerie = { + id: string + data: Datum[] +} & ExtraProps + +export interface ComputedCell { + id: string + serieId: string + value: number + formattedValue: string + data: Datum + x: number + y: number + width: number + height: number + color: string + opacity: number + borderColor: string + label: string + labelTextColor: string +} + +export interface CellAnimatedProps { + x: number + y: number + width: number + height: number + color: string + opacity: number + textColor: string + borderColor: string +} + +export type CellCanvasRenderer = ( + ctx: CanvasRenderingContext2D, + cell: ComputedCell +) => void + +export interface HeatMapDataProps { + data: HeatMapSerie[] +} + +export type LayerId = 'grid' | 'axes' | 'cells' | 'legends' +export interface CustomLayerProps { + cells: ComputedCell[] +} +export type CustomLayer = FunctionComponent> +export type CustomCanvasLayer = ( + ctx: CanvasRenderingContext2D, + props: CustomLayerProps +) => void + +export interface TooltipProps { + cell: ComputedCell +} +export type TooltipComponent = FunctionComponent> + +export interface CellComponentProps { + cell: ComputedCell + animated: AnimatedProps + onClick?: (cell: ComputedCell, event: MouseEvent) => void + onMouseEnter?: (cell: ComputedCell, event: MouseEvent) => void + onMouseMove?: (cell: ComputedCell, event: MouseEvent) => void + onMouseLeave?: (cell: ComputedCell, event: MouseEvent) => void +} +export type CellComponent = FunctionComponent> + +export type CellShape = 'rect' | 'circle' + +export type HeatMapCommonProps = { + minValue: number | 'auto' + maxValue: number | 'auto' + valueFormat: ValueFormat + + margin: Box + + forceSquare: boolean + sizeVariation: number + xInnerPadding: number + xOuterPadding: number + yInnerPadding: number + yOuterPadding: number + + opacity: number + activeOpacity: number + inactiveOpacity: number + borderWidth: number + borderColor: InheritedColorConfig, 'borderColor'>> + + enableGridX: boolean + enableGridY: boolean + axisTop: AxisProps | null + axisRight: AxisProps | null + axisBottom: AxisProps | null + axisLeft: AxisProps | null + + theme: Theme + colors: + | ContinuousColorScaleConfig + | (( + cell: Omit< + ComputedCell, + 'color' | 'opacity' | 'borderColor' | 'labelTextColor' + > + ) => string) + nanColor: string + + enableLabels: boolean + label: PropertyAccessor< + Omit, 'label' | 'color' | 'opacity' | 'borderColor' | 'labelTextColor'>, + string + > + labelTextColor: InheritedColorConfig, 'labelTextColor'>> + + legends: Omit[] + + isInteractive: boolean + hoverTarget: 'cell' | 'row' | 'column' | 'rowColumn' + tooltip: TooltipComponent + onClick: (cell: ComputedCell, event: MouseEvent) => void + + renderWrapper: boolean + + role: string + ariaLabel: AriaAttributes['aria-label'] + ariaLabelledBy: AriaAttributes['aria-labelledby'] + ariaDescribedBy: AriaAttributes['aria-describedby'] +} & Required + +export type HeatMapSvgProps = Partial< + HeatMapCommonProps +> & + HeatMapDataProps & + Dimensions & { + borderRadius?: number + layers?: (LayerId | CustomLayer)[] + cellComponent?: CellShape | CellComponent + onMouseEnter?: (cell: ComputedCell, event: MouseEvent) => void + onMouseMove?: (cell: ComputedCell, event: MouseEvent) => void + onMouseLeave?: (cell: ComputedCell, event: MouseEvent) => void + } + +export type HeatMapCanvasProps = Partial< + HeatMapCommonProps +> & + HeatMapDataProps & + Dimensions & { + layers?: (LayerId | CustomCanvasLayer)[] + renderCell?: CellShape | CellCanvasRenderer + pixelRatio?: number + } diff --git a/packages/heatmap/tsconfig.json b/packages/heatmap/tsconfig.json new file mode 100644 index 0000000000..855b4b2b74 --- /dev/null +++ b/packages/heatmap/tsconfig.json @@ -0,0 +1,8 @@ +{ + "extends": "../../tsconfig.types.json", + "compilerOptions": { + "outDir": "./dist/types", + "rootDir": "./src" + }, + "include": ["src/**/*"] +}