import { Iterable } from 'ix'; import * as React from 'react'; import { Col, Grid, Row } from 'react-bootstrap'; import { findDOMNode } from 'react-dom'; import { Observable, Subscription } from 'rxjs'; // the offset is needed to prevent the guide from interfering with mouse clicks export const BootstrapGuideOffset = 5; export class BootstrapGuide extends React.Component { private mouseMoveSub = Subscription.EMPTY; private guideRef = React.createRef(); private hlineRef = React.createRef(); private vlineRef = React.createRef(); componentDidMount() { const guide = this.guideRef.current; if (guide != null) { const hline = this.hlineRef.current; const vline = this.vlineRef.current; this.mouseMoveSub = Observable.fromEvent(document, 'mousemove') .do((e: MouseEvent) => { guide.style.visibility = e.shiftKey === true ? 'visible' : 'hidden'; }) .filter((e: MouseEvent) => e.shiftKey === true) .subscribe((e: MouseEvent) => { guide.style.top = `${this.calculateTop(guide, e)}px`; if (hline != null) { hline.style.top = `${Math.max( 0, e.pageY - BootstrapGuideOffset, )}px`; } if (vline != null) { vline.style.left = `${Math.max( 0, e.pageX - BootstrapGuideOffset, )}px`; } }); } } componentWillUnmount() { this.mouseMoveSub = Subscription.unsubscribe(this.mouseMoveSub); } render() { return (
{Iterable.range(1, 12) .map(x => (
{`Col ${x}`}
)) .toArray()}
); } private calculateTop(guide: HTMLDivElement, e: MouseEvent) { const parent = guide.offsetParent as HTMLDivElement; const offset = parent.offsetTop; const maxY = parent.clientHeight - guide.clientHeight; return Math.min( maxY, Math.max(0, e.pageY - guide.clientHeight - offset - BootstrapGuideOffset), ); } }