All files / src/ui Mark.tsx

100% Statements 9/9
80% Branches 8/10
100% Functions 3/3
100% Lines 9/9

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 1043x 3x                 3x                             3x     3x             9x   9x     9x                                                                                                   12x                          
import * as React from "react"
import { Portal } from "./Portal"
 
interface MarkContext {
  segmentOffset: number
  pxPerSec: number
  containerWidth: number
  regionsContainer?: HTMLElement | null
}
 
export const MarkContext = React.createContext<MarkContext>({
  segmentOffset: 0,
  pxPerSec: 0,
  containerWidth: 0,
  regionsContainer: null,
})
 
export interface MarkProps {
  children?: React.ReactNode
  duration: number
  offset: number
  color: string
  offsetFromSegment: boolean
}
 
export class Mark extends React.PureComponent<
  MarkProps & React.HTMLAttributes<HTMLDivElement>
> {
  static defaultProps = {
    duration: 0,
    color: "transparent",
    offsetFromSegment: false,
  }
 
  render() {
    const { children, offset, color, title } = this.props
 
    return (
      <MarkContext.Consumer>
        {({ pxPerSec, containerWidth, regionsContainer, segmentOffset }) =>
          offset * pxPerSec <= containerWidth ? (
            <>
              {children && (
                <div
                  style={{
                    ...this.positionElementStyles(
                      pxPerSec,
                      containerWidth,
                      this.props.offsetFromSegment ? segmentOffset : 0
                    ),
                    pointerEvents: "none",
                    zIndex: 6, // one layer above the waveform,
                  }}
                >
                  {children}
                </div>
              )}
              {regionsContainer && (
                <Portal node={regionsContainer}>
                  <div
                    title={title}
                    style={{
                      ...this.positionElementStyles(
                        pxPerSec,
                        containerWidth,
                        this.props.offsetFromSegment ? segmentOffset : 0
                      ),
                      backgroundColor: color,
                    }}
                  />
                </Portal>
              )}
            </>
          ) : null
        }
      </MarkContext.Consumer>
    )
  }
 
  /**
   * Position element in the waveform
   * @param {number} pxPerSec pixel per second ratio
   * @param {number} containerWidth waveform width
   * @return {React.CSSProperties} styles object
   */
  private positionElementStyles(
    pxPerSec: number,
    containerWidth: number,
    segmentOffset: number
  ): React.CSSProperties {
    return {
      position: "absolute",
      top: "0",
      left: (this.props.offset + segmentOffset) * pxPerSec + "px",
      width:
        Math.min(
          this.props.duration * pxPerSec,
          containerWidth - (this.props.offset + segmentOffset) * pxPerSec
        ) + "px",
      height: "100%",
    }
  }
}