UNPKG

@macrostrat/column-components

Version:

React rendering primitives for stratigraphic columns

120 lines (104 loc) 3.01 kB
import { Component, Context } from "react"; import { findDOMNode } from "react-dom"; import Box from "ui-box"; import h from "@macrostrat/hyper"; import { ColumnContext, ColumnCtx, ColumnDivision } from "../context"; interface ColumnScrollerProps { scrollToHeight: number; alignment: "center" | "top" | "bottom"; animated: boolean; onScrolled: (height: number) => void; paddingTop: number; scrollContainer: () => HTMLElement; } interface ScrollToOpts { animated?: boolean; alignment?: "center" | "top" | "bottom"; } const splitProps = function (keys, props) { const obj = {}; const rest = {}; for (let k in props) { const v = props[k]; if (keys.includes(k)) { obj[k] = v; } else { rest[k] = v; } } return [obj, rest]; }; export class ColumnScroller extends Component<ColumnScrollerProps> { constructor(props) { super(props); this.scrollTo = this.scrollTo.bind(this); } private static defaultProps: Partial<ColumnScrollerProps> = { animated: true, alignment: "center", onScrolled(height) { return console.log(`Scrolled to ${height} m`); }, scrollContainer() { return document.querySelector(".panel-container"); }, }; static contextType: Context<ColumnCtx<ColumnDivision>> = ColumnContext; context: ColumnCtx<ColumnDivision>; render() { const keys = [ "scrollToHeight", "alignment", "animated", "onScrolled", "paddingTop", "scrollContainer", ]; const [props, rest] = splitProps(keys, this.props); const { pixelHeight } = this.context; return h(Box, { height: pixelHeight, position: "absolute", ...rest, }); } scrollTo(height, opts: ScrollToOpts = {}) { let node = findDOMNode(this) as HTMLElement; let { animated, alignment, ...rest } = opts; if (animated == null) { animated = false; } const { paddingTop } = this.props; const { scale } = this.context; const pixelOffset = scale(height); const { top } = node.getBoundingClientRect(); node = this.props.scrollContainer(); let pos = pixelOffset + top + paddingTop; const screenHeight = window.innerHeight; if (this.props.alignment === "center") { pos -= screenHeight / 2; } else if (this.props.alignment === "bottom") { pos -= screenHeight; } return (node.scrollTop = pos); } componentDidMount() { const { scrollToHeight, alignment } = this.props; if (scrollToHeight == null) { return; } this.scrollTo(scrollToHeight, { alignment, animated: false }); return this.props.onScrolled(scrollToHeight); } componentDidUpdate(prevProps) { const { scrollToHeight, animated, alignment } = this.props; if (scrollToHeight == null) { return; } if (prevProps.scrollToHeight === scrollToHeight) { return; } this.scrollTo(scrollToHeight, { alignment, animated }); return this.props.onScrolled(scrollToHeight); } }