@macrostrat/column-components
Version:
React rendering primitives for stratigraphic columns
1 lines • 15.9 kB
Source Map (JSON)
{"version":3,"file":"edit-overlay.cjs","sources":["../src/edit-overlay.ts"],"sourcesContent":["import { format } from \"d3-format\";\nimport {\n Component,\n useContext,\n MouseEvent,\n ReactNode,\n createRef,\n RefObject,\n} from \"react\";\nimport h from \"./hyper\";\nimport { Popover, Position, Button, Intent } from \"@blueprintjs/core\";\nimport {\n ColumnLayoutContext,\n ColumnLayoutCtx,\n ColumnDivision,\n} from \"./context\";\nimport chroma from \"chroma-js\";\nimport { Box } from \"@macrostrat/ui-components\";\n\nconst fmt = format(\".1f\");\nconst fmt2 = format(\".2f\");\n\nconst IntervalNotification = function (props) {\n const { id, height, bottom, top, surface } = props;\n return h(\"div\", [\n h(\"h4\", `Section ${id} @ ${fmt(height)} m`),\n h(\"p\", [\"Interval ID: \", h(\"code\", id)]),\n h(\"p\", `${bottom} - ${top} m`),\n surface ? h(\"p\", [\"Surface: \", h(\"code\", surface)]) : null,\n ]);\n};\n\nconst PopoverEditorTitle = function (props) {\n const { interval, children } = props;\n return h(\"div.interval-editor-title\", [\n h(\"h3\", `${fmt2(interval.bottom)}–${fmt2(interval.top)} m`),\n h(\"div.id\", [h(\"code\", interval.id)]),\n children,\n ]);\n};\n\ntype color = string;\n\ninterface OverlayBoxProps {\n division: ColumnDivision;\n background: color;\n className: string;\n children?: ReactNode;\n onClick?: (evt: MouseEvent) => void;\n}\n\nconst OverlayBox = (props: OverlayBoxProps) => {\n const { division, background, className, onClick } = props;\n const { widthForDivision, scaleClamped } = useContext(ColumnLayoutContext);\n\n if (scaleClamped == null) return null;\n\n const top = scaleClamped(division.top);\n const bottom = scaleClamped(division.bottom);\n const height = bottom - top;\n\n const width = widthForDivision(division);\n\n const style = {\n marginTop: top,\n height,\n width,\n pointerEvents: \"none\",\n position: \"absolute\",\n };\n\n return h(\"div\", { style }, [\n h(\"div\", {\n onClick,\n className,\n style: {\n cursor: onClick != null ? \"pointer\" : null,\n width: \"100%\",\n height: \"100%\",\n background,\n },\n }),\n props.children,\n ]);\n};\n\nconst EditingBox = function ({ division, color, ...rest }) {\n if (division == null) {\n return null;\n }\n if (color == null) {\n color = \"red\";\n }\n const background = chroma(color).alpha(0.5).css();\n return h(OverlayBox, {\n className: \"editing-box\",\n division,\n background,\n ...rest,\n });\n};\n\ninterface DivisionEditOverlayProps {\n left: number;\n top: number;\n showInfoBox: boolean;\n onClick(ctx: {\n event?: MouseEvent;\n height: number;\n division?: ColumnDivision;\n }): void;\n allowEditing: boolean;\n renderEditorPopup(d: ColumnDivision): ReactNode;\n scaleToGrainsize: boolean;\n editingInterval: ColumnDivision;\n color: color;\n width: number;\n popoverWidth: number;\n selectedHeight: number;\n}\n\ninterface DivisionEditOverlayState {\n height: number | null;\n hoveredDivision: ColumnDivision | null;\n popoverIsOpen: boolean;\n}\n\nexport class DivisionEditOverlay extends Component<\n DivisionEditOverlayProps,\n DivisionEditOverlayState\n> {\n static contextType = ColumnLayoutContext;\n static defaultProps = {\n onHoverInterval() {},\n onClick() {},\n left: 0,\n top: 0,\n showInfoBox: false,\n allowEditing: true,\n renderEditorPopup() {\n return null;\n },\n color: \"red\",\n popoverWidth: 340,\n };\n\n elementRef: RefObject<HTMLDivElement>;\n\n timeout: any;\n declare context: ColumnLayoutCtx<ColumnDivision>;\n constructor(props: DivisionEditOverlayProps) {\n super(props);\n this.onHoverInterval = this.onHoverInterval.bind(this);\n this.removeHoverBox = this.removeHoverBox.bind(this);\n this.heightForEvent = this.heightForEvent.bind(this);\n this.onEditInterval = this.onEditInterval.bind(this);\n this.onClick = this.onClick.bind(this);\n this.renderCursorLine = this.renderCursorLine.bind(this);\n this.renderHoveredBox = this.renderHoveredBox.bind(this);\n this.closePopover = this.closePopover.bind(this);\n this.state = {\n height: null,\n hoveredDivision: null,\n popoverIsOpen: false,\n };\n this.timeout = null;\n\n this.elementRef = createRef();\n }\n\n onHoverInterval(event) {\n event.stopPropagation();\n // findDOMNode might be slow but I'm not sure\n if (this.elementRef.current !== event.target) {\n return;\n }\n const height = this.heightForEvent(event);\n this.setState({ height });\n if (!this.props.allowEditing) {\n return;\n }\n const { divisions } = this.context;\n\n let division = null;\n for (let d of Array.from(divisions)) {\n if (d.bottom <= height && height < d.top) {\n division = d;\n break;\n }\n }\n if (division === this.state.hoveredDivision) {\n return;\n }\n this.setState({ hoveredDivision: division });\n if (this.timeout != null) {\n clearTimeout(this.timeout);\n return (this.timeout = null);\n }\n }\n\n removeHoverBox() {\n this.setState({ hoveredDivision: null, popoverIsOpen: false });\n return (this.timeout = null);\n }\n\n heightForEvent(event) {\n const { scale } = this.context;\n const { offsetY } = event.nativeEvent;\n return scale.invert(offsetY);\n }\n\n onEditInterval(event) {\n if (this.state.popoverIsOpen) {\n return;\n }\n // This could be moved to the actual interval\n // wrapped with a withRouter\n const { showInfoBox } = this.props;\n const { hoveredDivision } = this.state;\n const height = this.heightForEvent(event);\n event.stopPropagation();\n if (event.shiftKey && showInfoBox) {\n this.setState({ popoverIsOpen: true });\n return;\n }\n return this.props.onClick({ event, height, division: hoveredDivision });\n }\n\n onClick(event) {\n // This event handler might be unnecessary\n if (this.props.allowEditing) {\n return this.onEditInterval(event);\n }\n const height = this.heightForEvent(event);\n return this.props.onClick({ height });\n }\n\n renderCursorLine() {\n let { height, hoveredDivision } = this.state;\n const { scaleClamped } = this.context;\n // Show the height we have selected if we are not hovering\n const { selectedHeight } = this.props;\n if (height == null) {\n height = selectedHeight;\n }\n if (height == null) {\n return;\n }\n const style = {\n top: scaleClamped(height),\n height: 0,\n border: \"0.5px solid black\",\n width: this.context.widthForDivision(hoveredDivision),\n position: \"absolute\",\n pointerEvents: \"none\",\n };\n\n return h(\"div.cursor\", { style }, [\n h(\n \"div.cursor-position\",\n {\n style: {\n pointerEvents: \"none\",\n fontWeight: \"bold\",\n fontSize: \"12px\",\n left: \"2px\",\n top: \"-14px\",\n position: \"absolute\",\n color: \"black\",\n },\n },\n [fmt2(height)],\n ),\n ]);\n }\n\n renderHoveredBox() {\n if (this.state.hoveredDivision == null) {\n return null;\n }\n const { popoverIsOpen, hoveredDivision: division } = this.state;\n const width = this.context.widthForDivision(division);\n const { color } = this.props;\n const background = chroma(color).alpha(0.3).css();\n\n return h(\n OverlayBox,\n {\n division,\n className: \"hovered-box\",\n background,\n },\n [\n h.if(this.props.renderEditorPopup != null)(\n Popover,\n {\n isOpen: popoverIsOpen && division != null,\n //style: { display: \"block\", width },\n position: Position.LEFT,\n },\n [\n h(\"div\", {\n style: { width, height: 30, transform: \"translate(0,-30)\" },\n }),\n h(\n \"div.editor-popover-contents\",\n {\n style: {\n width: this.props.popoverWidth,\n padding: \"10px\",\n },\n },\n [\n h(\n PopoverEditorTitle,\n {\n interval: division,\n },\n [\n h(Button, {\n icon: \"cross\",\n minimal: true,\n intent: Intent.WARNING,\n onClick: this.closePopover.bind(this),\n }),\n ],\n ),\n this.props.renderEditorPopup(division),\n ],\n ),\n ],\n ),\n ],\n );\n }\n\n closePopover() {\n return this.setState({\n popoverIsOpen: false,\n });\n }\n\n render() {\n let { divisions, pixelHeight, width } = this.context;\n const { popoverIsOpen, hoveredDivision: division } = this.state;\n const { left, top, color } = this.props;\n if (width == null) {\n ({ width } = this.props);\n }\n\n return h(\n Box,\n {\n className: \"edit-overlay\",\n width,\n height: pixelHeight,\n ref: this.elementRef,\n style: {\n left,\n top,\n position: \"absolute\",\n zIndex: 18,\n pointerEvents: \"all\",\n cursor: \"pointer\",\n },\n onClick: this.onEditInterval,\n onMouseEnter: this.onHoverInterval,\n onMouseMove: this.onHoverInterval,\n onMouseLeave: () => {\n if (popoverIsOpen) {\n return;\n }\n this.setState({ height: null });\n return (this.timeout = setTimeout(this.removeHoverBox, 1000));\n },\n },\n [\n this.renderHoveredBox(),\n h(EditingBox, {\n division: this.props.editingInterval,\n color,\n }),\n this.renderCursorLine(),\n ],\n );\n }\n}\n"],"names":["format","h","useContext","ColumnLayoutContext","Component","createRef","Popover","Position","Button","Intent","Box"],"mappings":"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"}