UNPKG

@macrostrat/column-components

Version:

React rendering primitives for stratigraphic columns

304 lines (303 loc) 8.33 kB
"use strict"; Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" }); const d3Format = require("d3-format"); const React = require("react"); const hyper = require("./hyper.cjs"); const core = require("@blueprintjs/core"); require("./chroma-js/index.cjs"); const uiComponents = require("@macrostrat/ui-components"); const chroma = require("./chroma-js/src/chroma.cjs"); const layout = require("./context/layout.cjs"); d3Format.format(".1f"); const fmt2 = d3Format.format(".2f"); const PopoverEditorTitle = function(props) { const { interval, children } = props; return hyper("div.interval-editor-title", [ hyper("h3", `${fmt2(interval.bottom)}–${fmt2(interval.top)} m`), hyper("div.id", [hyper("code", interval.id)]), children ]); }; const OverlayBox = (props) => { const { division, background, className, onClick } = props; const { widthForDivision, scaleClamped } = React.useContext(layout.ColumnLayoutContext); if (scaleClamped == null) return null; const top = scaleClamped(division.top); const bottom = scaleClamped(division.bottom); const height = bottom - top; const width = widthForDivision(division); const style = { marginTop: top, height, width, pointerEvents: "none", position: "absolute" }; return hyper("div", { style }, [ hyper("div", { onClick, className, style: { cursor: onClick != null ? "pointer" : null, width: "100%", height: "100%", background } }), props.children ]); }; const EditingBox = function({ division, color, ...rest }) { if (division == null) { return null; } if (color == null) { color = "red"; } const background = chroma(color).alpha(0.5).css(); return hyper(OverlayBox, { className: "editing-box", division, background, ...rest }); }; class DivisionEditOverlay extends React.Component { static contextType = layout.ColumnLayoutContext; static defaultProps = { onHoverInterval() { }, onClick() { }, left: 0, top: 0, showInfoBox: false, allowEditing: true, renderEditorPopup() { return null; }, color: "red", popoverWidth: 340 }; elementRef; timeout; constructor(props) { super(props); this.onHoverInterval = this.onHoverInterval.bind(this); this.removeHoverBox = this.removeHoverBox.bind(this); this.heightForEvent = this.heightForEvent.bind(this); this.onEditInterval = this.onEditInterval.bind(this); this.onClick = this.onClick.bind(this); this.renderCursorLine = this.renderCursorLine.bind(this); this.renderHoveredBox = this.renderHoveredBox.bind(this); this.closePopover = this.closePopover.bind(this); this.state = { height: null, hoveredDivision: null, popoverIsOpen: false }; this.timeout = null; this.elementRef = React.createRef(); } onHoverInterval(event) { event.stopPropagation(); if (this.elementRef.current !== event.target) { return; } const height = this.heightForEvent(event); this.setState({ height }); if (!this.props.allowEditing) { return; } const { divisions } = this.context; let division = null; for (let d of Array.from(divisions)) { if (d.bottom <= height && height < d.top) { division = d; break; } } if (division === this.state.hoveredDivision) { return; } this.setState({ hoveredDivision: division }); if (this.timeout != null) { clearTimeout(this.timeout); return this.timeout = null; } } removeHoverBox() { this.setState({ hoveredDivision: null, popoverIsOpen: false }); return this.timeout = null; } heightForEvent(event) { const { scale } = this.context; const { offsetY } = event.nativeEvent; return scale.invert(offsetY); } onEditInterval(event) { if (this.state.popoverIsOpen) { return; } const { showInfoBox } = this.props; const { hoveredDivision } = this.state; const height = this.heightForEvent(event); event.stopPropagation(); if (event.shiftKey && showInfoBox) { this.setState({ popoverIsOpen: true }); return; } return this.props.onClick({ event, height, division: hoveredDivision }); } onClick(event) { if (this.props.allowEditing) { return this.onEditInterval(event); } const height = this.heightForEvent(event); return this.props.onClick({ height }); } renderCursorLine() { let { height, hoveredDivision } = this.state; const { scaleClamped } = this.context; const { selectedHeight } = this.props; if (height == null) { height = selectedHeight; } if (height == null) { return; } const style = { top: scaleClamped(height), height: 0, border: "0.5px solid black", width: this.context.widthForDivision(hoveredDivision), position: "absolute", pointerEvents: "none" }; return hyper("div.cursor", { style }, [ hyper( "div.cursor-position", { style: { pointerEvents: "none", fontWeight: "bold", fontSize: "12px", left: "2px", top: "-14px", position: "absolute", color: "black" } }, [fmt2(height)] ) ]); } renderHoveredBox() { if (this.state.hoveredDivision == null) { return null; } const { popoverIsOpen, hoveredDivision: division } = this.state; const width = this.context.widthForDivision(division); const { color } = this.props; const background = chroma(color).alpha(0.3).css(); return hyper( OverlayBox, { division, className: "hovered-box", background }, [ hyper.if(this.props.renderEditorPopup != null)( core.Popover, { isOpen: popoverIsOpen && division != null, //style: { display: "block", width }, position: core.Position.LEFT }, [ hyper("div", { style: { width, height: 30, transform: "translate(0,-30)" } }), hyper( "div.editor-popover-contents", { style: { width: this.props.popoverWidth, padding: "10px" } }, [ hyper( PopoverEditorTitle, { interval: division }, [ hyper(core.Button, { icon: "cross", minimal: true, intent: core.Intent.WARNING, onClick: this.closePopover.bind(this) }) ] ), this.props.renderEditorPopup(division) ] ) ] ) ] ); } closePopover() { return this.setState({ popoverIsOpen: false }); } render() { let { divisions, pixelHeight, width } = this.context; const { popoverIsOpen, hoveredDivision: division } = this.state; const { left, top, color } = this.props; if (width == null) { ({ width } = this.props); } return hyper( uiComponents.Box, { className: "edit-overlay", width, height: pixelHeight, ref: this.elementRef, style: { left, top, position: "absolute", zIndex: 18, pointerEvents: "all", cursor: "pointer" }, onClick: this.onEditInterval, onMouseEnter: this.onHoverInterval, onMouseMove: this.onHoverInterval, onMouseLeave: () => { if (popoverIsOpen) { return; } this.setState({ height: null }); return this.timeout = setTimeout(this.removeHoverBox, 1e3); } }, [ this.renderHoveredBox(), hyper(EditingBox, { division: this.props.editingInterval, color }), this.renderCursorLine() ] ); } } exports.DivisionEditOverlay = DivisionEditOverlay; //# sourceMappingURL=edit-overlay.cjs.map