{"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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"}