UNPKG

terriajs

Version:

Geospatial data visualization platform.

150 lines (137 loc) 4.02 kB
import { observer } from "mobx-react"; import { computed } from "mobx"; import { Tooltip as VisxTooltip } from "@visx/tooltip"; import { CSSTransition } from "react-transition-group"; import PropTypes from "prop-types"; import React from "react"; import dateformat from "dateformat"; import groupBy from "lodash-es/groupBy"; import Styles from "./tooltip.scss"; @observer class Tooltip extends React.Component { static propTypes = { items: PropTypes.array.isRequired, left: PropTypes.number, right: PropTypes.number, top: PropTypes.number, bottom: PropTypes.number }; prevItems = []; @computed get items() { // When items` is unset, hold on to its last value. We do this because we // want to keep showing the tooltip. We then fade it out using the // CSSTransition below. const items = this.props.items; if (items && items.length > 0) { this.prevItems = items; return items; } else { return this.prevItems; } } @computed get title() { const items = this.items; if (items.length > 0) { // derive title from first item x const x = items[0].point.x; return x instanceof Date ? dateformat(x, "dd/mm/yyyy, HH:MMTT") : x; } else return undefined; } @computed get groups() { // momentLines and momentPoints are not shown in the tooltip body const tooltipItems = this.items.filter( ({ chartItem }) => chartItem.type !== "momentLines" && chartItem.type !== "momentPoints" ); return Object.entries(groupBy(tooltipItems, "chartItem.categoryName")).map( (o) => ({ name: o[0], items: o[1] }) ); } @computed get style() { const { left, right, top, bottom } = this.props; return { left: left === undefined ? "" : `${left}px`, right: right === undefined ? "" : `${right}px`, top: top === undefined ? "" : `${top}px`, bottom: bottom === undefined ? "" : `${bottom}px`, position: "absolute", boxShadow: "0 1px 2px rgba(33,33,33,0.2)" }; } render() { const { items } = this.props; const show = items.length > 0; return ( <CSSTransition in={show} classNames="transition" timeout={1000} unmountOnExit > <VisxTooltip className={Styles.tooltip} key={Math.random()} style={this.style} > <div className={Styles.title}>{this.title}</div> <div> <For each="group" of={this.groups}> <TooltipGroup key={`tooltip-group-${group.name}`} name={this.groups.length > 1 ? group.name : undefined} items={group.items} /> </For> </div> </VisxTooltip> </CSSTransition> ); } } class TooltipGroup extends React.PureComponent { static propTypes = { name: PropTypes.string, items: PropTypes.array.isRequired }; render() { const { name, items } = this.props; return ( <div className={Styles.group}> {name && <div className={Styles.groupName}>{name}</div>} <For each="item" of={items}> <TooltipItem key={`tooltipitem-${item.chartItem.key}`} item={item} /> </For> </div> ); } } @observer class TooltipItem extends React.Component { static propTypes = { item: PropTypes.object.isRequired }; render() { const chartItem = this.props.item.chartItem; const value = this.props.item.point.y; const formattedValue = isNaN(value) ? value : value.toFixed(2); return ( <div className={Styles.item}> <div className={Styles.itemSymbol} style={{ backgroundColor: chartItem.getColor() }} /> <div className={Styles.itemName}>{chartItem.name}</div> <div className={Styles.itemValue}>{formattedValue}</div> <div className={Styles.itemUnits}>{chartItem.units}</div> </div> ); } } export default Tooltip;