Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Timeline diana #12

Open
wants to merge 9 commits into
base: timeline-diana
Choose a base branch
from
22 changes: 19 additions & 3 deletions packages/cbioportal-clinical-timeline/src/Timeline.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,11 +27,15 @@ import { DownloadControls } from 'cbioportal-frontend-commons';
import CustomTrack, { CustomTrackSpecification } from './CustomTrack';
import CustomTrackHeader from './CustomTrackHeader';
import { TIMELINE_TRACK_HEIGHT } from './TimelineTrack';
import classNames from 'classnames';

interface ITimelineProps {
store: TimelineStore;
customTracks?: CustomTrackSpecification[];
width: number;
hideLabels?: boolean;
visibleTrackTypes?: string[];
disableTrackHover?: boolean;
}

const getFocusedPoints = _.debounce(function(
Expand Down Expand Up @@ -235,8 +239,15 @@ const Timeline: React.FunctionComponent<ITimelineProps> = observer(function({
store,
customTracks,
width,
hideLabels = false,
visibleTrackTypes,
disableTrackHover,
}: ITimelineProps) {
const expandedTracks = expandTracks(store.data);
const expandedTracks = expandTracks(
store.data,
undefined,
visibleTrackTypes
);
const height =
TICK_AXIS_HEIGHT +
_.sumBy(expandedTracks, t => t.height) +
Expand All @@ -256,7 +267,7 @@ const Timeline: React.FunctionComponent<ITimelineProps> = observer(function({

const memoizedHoverCallback = useCallback(
(e: React.MouseEvent) => {
hoverCallback(e, refs.hoverStyleTag);
if (!disableTrackHover) hoverCallback(e, refs.hoverStyleTag);
},
[refs.hoverStyleTag]
);
Expand Down Expand Up @@ -307,7 +318,11 @@ const Timeline: React.FunctionComponent<ITimelineProps> = observer(function({
className={'tl-timeline-leftbar'}
style={{ paddingTop: TICK_AXIS_HEIGHT, flexShrink: 0 }}
>
<div className={'tl-timeline-tracklabels'}>
<div
className={classNames('tl-timeline-tracklabels', {
'tl-displaynone': hideLabels,
})}
>
{expandedTracks.map(track => {
return (
<TrackHeader
Expand Down Expand Up @@ -370,6 +385,7 @@ const Timeline: React.FunctionComponent<ITimelineProps> = observer(function({
width={renderWidth}
customTracks={customTracks}
handleTrackHover={memoizedHoverCallback}
visibleTrackTypes={visibleTrackTypes}
/>
<TickAxis
store={store}
Expand Down
52 changes: 51 additions & 1 deletion packages/cbioportal-clinical-timeline/src/TimelineStore.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ import {
getTrimmedTicks,
} from './lib/helpers';
import _ from 'lodash';
import jQuery from 'jquery';
import autobind from 'autobind-decorator';
import * as React from 'react';
import {
Expand All @@ -31,9 +30,60 @@ export class TimelineStore {
events: TimelineEvent[];
index: number;
};
@observable groupByOption: Readonly<string> | null = null;
@observable onlyShowSelectedInVAFChart:
| Readonly<boolean>
| undefined = undefined;
@observable vafChartLogScale: Readonly<boolean> | undefined = undefined;
@observable vafChartYAxisToDataRange:
| Readonly<boolean>
| undefined = undefined;
@observable vafChartHeight: Readonly<number> = 240;

@observable mousePosition = { x: 0, y: 0 };

@action
setVafChartHeight(value: number) {
this.vafChartHeight = value;
}

@action
setGroupByOption(value: string) {
this.groupByOption = value;
}

@action
setOnlyShowSelectedInVAFChart(value: boolean) {
this.onlyShowSelectedInVAFChart = value;
}

@action
setVafChartLogScale(value: boolean) {
this.vafChartLogScale = value;
}

@action
setVafChartYAxisToDataRange(value: boolean) {
this.vafChartYAxisToDataRange = value;
}

@computed get xPositionBySampleId(): { [sampleId: string]: number } {
let positionList: { [sampleId: string]: number } = {};
const samples = this.allItems.filter(
event => event.event.eventType === 'SPECIMEN'
);
samples.forEach((sample, i) => {
sample.event.attributes.forEach((attribute: any, i: number) => {
if (attribute.key === 'SAMPLE_ID') {
positionList[attribute.value] = this.getPosition(
sample
)!.pixelLeft;
}
});
});
return positionList;
}

@autobind
@action
setTooltipModel(
Expand Down
12 changes: 9 additions & 3 deletions packages/cbioportal-clinical-timeline/src/TimelineTrack.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import React from 'react';
import _ from 'lodash';
import { formatDate, REMOVE_FOR_DOWNLOAD_CLASSNAME } from './lib/helpers';
import { TimelineStore } from './TimelineStore';
import { renderStack } from './svg/renderStack';

export interface ITimelineTrackProps {
trackData: TimelineTrackSpecification;
Expand Down Expand Up @@ -103,7 +104,7 @@ function renderSuperscript(number: number) {
);
}

function renderLineChartLine(points: { x: number; y: number }[]) {
function renderLineChartLines(points: { x: number; y: number }[]) {
if (points.length < 2) {
return null;
}
Expand Down Expand Up @@ -170,7 +171,11 @@ export function renderPoint(
return (
<g>
{events.length > 1 && renderSuperscript(events.length)}
<circle cx="0" cy={y} r="4" fill="rgb(31, 119, 180)" />
{events.length > 1 ? (
renderStack(10, TIMELINE_TRACK_HEIGHT / 2, '#222222')
) : (
<circle cx="0" cy={y} r="4" fill="rgb(31, 119, 180)" />
)}
</g>
);
}
Expand Down Expand Up @@ -289,7 +294,8 @@ export const TimelineTrack: React.FunctionComponent<
store.setTooltipModel(null);
}}
/>
{renderLineChartLine(linePoints)}
{trackData.trackType === TimelineTrackType.LINE_CHART &&
renderLineChartLines(linePoints)}
{points}
<line
x1={0}
Expand Down
48 changes: 34 additions & 14 deletions packages/cbioportal-clinical-timeline/src/TimelineTracks.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,19 @@ export interface ITimelineTracks {
width: number;
handleTrackHover: (e: React.MouseEvent<SVGGElement>) => void;
customTracks?: CustomTrackSpecification[];
visibleTrackTypes?: string[];
}

export const TimelineTracks: React.FunctionComponent<
ITimelineTracks
> = observer(function({ store, width, handleTrackHover, customTracks }) {
const tracks = expandTracks(store.data);
> = observer(function({
store,
width,
handleTrackHover,
customTracks,
visibleTrackTypes,
}) {
const tracks = expandTracks(store.data, undefined, visibleTrackTypes);

let nextY = 0;

Expand Down Expand Up @@ -65,18 +72,31 @@ export const TimelineTracks: React.FunctionComponent<
);
})}
</g>
{store.tooltipContent && (
<Portal container={document.body}>
<Popover
arrowOffsetTop={17}
className={'tl-timeline-tooltip'}
positionLeft={store.mousePosition.x + 10}
positionTop={store.mousePosition.y - 17}
>
{store.tooltipContent}
</Popover>
</Portal>
)}
{store.tooltipContent &&
(() => {
const placementLeft = store.mousePosition.x > width / 2;
return (
<Portal container={document.body}>
<Popover
arrowOffsetTop={17}
placement={placementLeft ? 'left' : 'right'}
style={{
transform: placementLeft
? 'translate(-100%, 0)'
: '',
}}
className={'tl-timeline-tooltip'}
positionLeft={
store.mousePosition.x +
(placementLeft ? -10 : 10)
}
positionTop={store.mousePosition.y - 17}
>
{store.tooltipContent}
</Popover>
</Portal>
);
})()}
</>
);
});
Expand Down
15 changes: 11 additions & 4 deletions packages/cbioportal-clinical-timeline/src/TrackHeader.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import { TimelineStore } from './TimelineStore';

interface ITrackHeaderProps {
track: TimelineTrackSpecification;
handleTrackHover: (e: React.MouseEvent<any>) => void;
handleTrackHover?: (e: React.MouseEvent<any>) => void;
height: number;
paddingLeft?: number;
}
Expand Down Expand Up @@ -57,13 +57,20 @@ function expandTrack(

export function expandTracks(
tracks: TimelineTrackSpecification[],
leftPadding: number | undefined = 5
leftPadding: number | undefined = 5,
visibleTrackTypes?: string[]
) {
return _.flatMap(tracks, t => expandTrack(t, leftPadding));
const flattened = _.flatMap(tracks, t => expandTrack(t, leftPadding));

if (visibleTrackTypes) {
return flattened.filter(t => visibleTrackTypes.includes(t.track.type));
} else {
return flattened;
}
}

export const EXPORT_TRACK_HEADER_STYLE =
'font-size: 12px;text-transform: capitalize; font-family:Arial';
'font-size: 12px;text-transform: uppercase; font-family:Arial';
export const EXPORT_TRACK_HEADER_BORDER_CLASSNAME = 'track-header-border';

export function getTrackHeadersG(
Expand Down
53 changes: 53 additions & 0 deletions packages/cbioportal-clinical-timeline/src/svg/renderStack.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import React from 'react';

const SHEET_HEIGHT_OVER_WIDTH = 0.68;

function renderCenteredSheet(width:number, y:number, fill:string, strokeWidth=0) {
const left = -width/2;
const right = width/2;
const top = SHEET_HEIGHT_OVER_WIDTH * left;
const bottom = SHEET_HEIGHT_OVER_WIDTH * right;

return (
<path
d={`M ${left} ${y} L 0 ${y + top} L ${right} ${y} L 0 ${y + bottom} L ${left} ${y} Z`}
strokeWidth={strokeWidth}
stroke={fill}
fill={fill}
/>
);
}

function renderMaskedSheet(width:number, y:number, fill:string) {
const maskProportion = 0.81;
const height = SHEET_HEIGHT_OVER_WIDTH * width;
return (
<>
{renderCenteredSheet(width, y, fill)}
<g style={{
transform:`translate(0, -${(1-maskProportion) * height/2}px)`
}}>
{renderCenteredSheet(maskProportion * width, y, "#fff", 0.3)}
</g>
</>
);
}

export function renderStack(width:number, y:number, fills:string|string[]) {
fills = ([] as string[]).concat(fills);

// ensure 3 fills
if (fills.length === 1) {
fills = [fills[0], fills[0], fills[0]];
} else if (fills.length === 2) {
fills = [fills[0], fills[1], fills[0]];
}

return (
<g>
{renderMaskedSheet(width, y + width/4.5, fills[0])}
{renderMaskedSheet(width, y, fills[1])}
{renderCenteredSheet(width, y - width/4.5, fills[2])}
</g>
)
}
6 changes: 5 additions & 1 deletion packages/cbioportal-clinical-timeline/src/timeline.scss
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ $borderColor: #ccc;
.tl-timeline-leftbar {
.tl-timeline-tracklabels {
font-size: 12px;
text-transform: capitalize;
text-transform: uppercase;
> div {
white-space: nowrap;
border-bottom: 1px dashed #eee;
Expand Down Expand Up @@ -130,3 +130,7 @@ $borderColor: #ccc;
.timeline-label:last-of-type {
display: none;
}

.tl-displaynone {
display: none;
}
16 changes: 16 additions & 0 deletions src/globalStyles/global.scss
Original file line number Diff line number Diff line change
Expand Up @@ -631,3 +631,19 @@ h6.blackHeader {
.nowrap {
white-space: nowrap;
}

.standardMarginTop {
margin-top: $standardMargin;
}

.standardMarginBottom {
margin-bottom: $standardMargin;
}

.standardMarginLeft {
margin-left: $standardMargin;
}

.standardMarginRight {
margin-right: $standardMargin;
}
Loading