import React, { useMemo, useRef, useEffect, useContext, useCallback } from 'react'; import { v1 as uuidv1 } from 'uuid'; import { SummaryStatisticSNP, ManhattanSNP } from './types'; import { xtransform, ytransform } from '../../../utils/coordinates'; import { ClipPath } from '../../clippath'; import { renderManhattanPlotData } from './utils'; import { TooltipContext } from "../../tooltip/tooltipcontext"; import { svgPoint } from '../../../utils/svg'; import TooltipContent from './tooltip'; import { Domain } from 'utils/types'; export type ManhattanTrackProps = { width: number; height: number; id?: string; transform?: string; domain: Domain; data?: SummaryStatisticSNP[]; svgRef?: React.RefObject; threshold?: number; max?: number; onHeightChanged?: (height: number) => void; tooltipContent?: React.FC; snpProps?: (snp: ManhattanSNP, props: ManhattanTrackProps) => React.SVGProps; onSNPClick?: (snp: ManhattanSNP) => void; onSNPMousedOver?: (snp: ManhattanSNP) => void; onSNPMousedOut?: () => void; sortOrder?: (a: ManhattanSNP, b: ManhattanSNP) => number; className?: string; }; export const defaultManhattanSNPProps = (snp: ManhattanSNP, props: ManhattanTrackProps): React.SVGProps => ({ fill: snp.data.score < 1e-8 ? "#ff00000" : "#888888", r: snp.data.score < 1e-8 ? props.height / 30 : props.height / 40 }); const ManhattanTrack: React.FC = props => { const tooltipContext = useContext(TooltipContext); const Tooltip = props.tooltipContent || TooltipContent; const mouseOver = useCallback((event: React.MouseEvent, snp: ManhattanSNP) => { if (!props.svgRef || !props.svgRef.current) return; const [ x, y ] = svgPoint(props.svgRef.current, event); tooltipContext.setTooltip({ show: true, x: x > props.width - 100 ? x - 90 : x + 10, y: y + 10, width: 100, height: 50, content: }); }, [ Tooltip, props ]); const mouseOut = useCallback(() => { props.onSNPMousedOut && props.onSNPMousedOut(); tooltipContext.setTooltip({ show: false, x: 0, y: 0, content: undefined, width: 0, height: 0 }); }, [ tooltipContext ]); const uuid = useRef(uuidv1()); const max = props.max || 20; const x = useCallback(xtransform(props.domain, props.width), [ props ]); const y = useCallback(ytransform({ min: 0, max }, props.height), [ props ]); const id = props.id || uuid.current.toString(); const circleProps = useCallback(props.snpProps ? ( (x: ManhattanSNP, props: ManhattanTrackProps) => ({ ...defaultManhattanSNPProps(x, props), ...props.snpProps!(x, props) }) ) : defaultManhattanSNPProps, [ props ]); const rendered: ManhattanSNP[] = useMemo( () => renderManhattanPlotData(props.data!, x, v => y(v > max ? max : v)).sort(props.sortOrder || ((a, b) => (a.data.score - b.data.score))), [ props.domain, props.width, props.data ] ); useEffect( () => { props.onHeightChanged && props.onHeightChanged(props.height); }, [ props.height ]); return ( { rendered.map( (snp, i) => ( props.onSNPClick && props.onSNPClick(snp)} onMouseOut={mouseOut} onMouseOver={(e: React.MouseEvent) => { e.persist(); props.onSNPMousedOver && props.onSNPMousedOver(snp); mouseOver(e, snp); }} /> ))} ); } export default ManhattanTrack;