terriajs
Version:
Geospatial data visualization platform.
150 lines (137 loc) • 4.02 kB
JSX
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";
class Tooltip extends React.Component {
static propTypes = {
items: PropTypes.array.isRequired,
left: PropTypes.number,
right: PropTypes.number,
top: PropTypes.number,
bottom: PropTypes.number
};
prevItems = [];
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;
}
}
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;
}
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]
})
);
}
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>
);
}
}
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;