import React, { useState, useContext, useCallback } from 'react'; import { LoadingSpinner, ErrorIcon } from '../../icons'; import TrackMargin from './margin'; import { WrappedTrackProps } from './types'; import { isReactElement } from '../../../utils'; import { ContextMenuContext } from "../../contextmenu/contextmenu"; import { downloadSVG, svgPoint } from '../../../utils/svg'; const getShortLabel = (shortLabel: string, title: string) => { if (shortLabel) return shortLabel; if (!title || !title.substring || !title.length) return ""; return title.length <= 20 ? title : title.substring(0, 20) + "..."; } /** * A data track with an associated margin and title. The track also displays a single data range * if and only if at least one of the child tracks stores a range in the rendered.range field of * the rendered range it passes to its onDataRendered callback. The range displayed is the minumum of * all rendered.range minima for the children to the maximum of all rendered.range maxima for the children. */ const WrappedTrack: React.FC = props => { const contextMenuContext = useContext(ContextMenuContext); const download = () => { const g = props.svgRef?.current?.getElementById(props.id) as SVGSVGElement const ruler = props.svgRef?.current?.getElementById('ruler') as SVGSVGElement downloadSVG(g, ruler, props.id+".svg"); }; const [ height, setHeight ] = useState(props.height || 0); const onHeightChanged = useCallback((height: number) => { setHeight(height); props.onHeightChanged && props.onHeightChanged(height); }, [ props ]); React.useEffect(() => { props.onHeightChanged && props.height!==undefined && props.onHeightChanged(props.height); }, [ props.height ]) const trackMargin = props.trackMargin || (props.height || props.rowHeight || height) / 6; const titleSize = props.titleSize || trackMargin * 2.5; const titleMargin = props.title ? titleSize + 5 : 0; const totalVerticalMargin = trackMargin + titleMargin; const marginLabel = getShortLabel(props.shortLabel || "", props.title || ""); const childHeight = height - totalVerticalMargin; const marginSize = props.width < 1000 ? props.width / 8 : 150; const childWidth = props.width - marginSize; const spinnerSize = height < 80 ? height / 2 : 25; const mode = props.displayMode?.toLowerCase(); const displayMode = mode; return ( { contextMenuContext.setContextMenu({ show: false, x: 0, y: 0, id: '' }); }} onContextMenu={event => { if(props.trackType!=='ruler'){ event.preventDefault(); if(props.svgRef) { let mousePosition = svgPoint(props.svgRef.current!,event) contextMenuContext.setContextMenu({ show: true, x: mousePosition[0] > props.width - 80 ? mousePosition[0] - 80: mousePosition[0] , y: mousePosition[1], id: props.id, displayMode, downloadSVG: download, displayModeOptions: props.trackType === 'bigbed' || props.trackType === 'bam' ? ['dense','squish','hide','download'] : props.trackType==='bigwig' ? ['full','dense','hide' , 'download'] : props.trackType==='transcript' ? ['pack','squish','hide','download']: props.trackType==='ldtrack' ? ['dense','hide','download'] : ['hide','download'] }); } } }}> {props.height!=0 && } { props.loading && !props.error && ( )} { props.error && ( Error loading track )} { !props.loading && !props.error && props.title && ( {props.title} )} {!props.loading && !props.error && { React.Children.map( props.children, (child: React.ReactNode) => ( isReactElement(child) ? ( React.cloneElement( child, { ...child.props, height: childHeight, width: props.width - marginSize, onHeightChanged: (h: number) => { // child.props.onHeightChanged && child.props.onHeightChanged(h); onHeightChanged(h + totalVerticalMargin); } } ) ) : child ))} } ); } export default WrappedTrack;