import * as React from 'react';
import { observer, Observer } from 'mobx-react';
import { action, computed, observable, makeObservable, autorun } from 'mobx';
import Oncoprint from '../../../../shared/components/oncoprint/Oncoprint';
import OncoprintControls, {
IOncoprintControlsHandlers,
IOncoprintControlsState,
} from 'shared/components/oncoprint/controls/OncoprintControls';
import { percentAltered } from '../../../../shared/components/oncoprint/OncoprintUtils';
import { getServerConfig } from 'config/config';
import { OncoprintJS } from 'oncoprintjs';
import fileDownload from 'react-file-download';
import { FadeInteraction, svgToPdfDownload } from 'cbioportal-frontend-commons';
import classNames from 'classnames';
import OncoprinterStore from './OncoprinterStore';
import autobind from 'autobind-decorator';
import { onMobxPromise } from 'cbioportal-frontend-commons';
import WindowStore from '../../../../shared/components/window/WindowStore';
import { getGeneticTrackKey } from './OncoprinterGeneticUtils';
import InfoBanner from '../../../../shared/components/banners/InfoBanner';
import '../../../../globalStyles/oncoprintStyles.scss';
interface IOncoprinterProps {
divId: string;
store: OncoprinterStore;
}
@observer
export default class Oncoprinter extends React.Component<
IOncoprinterProps,
{}
> {
@observable distinguishMutationType: boolean = true;
@observable distinguishGermlineMutations = true;
@observable sortByMutationType: boolean = true;
@observable sortByDrivers: boolean = true;
@observable showWhitespaceBetweenColumns: boolean = true;
@observable showClinicalTrackLegends: boolean = true;
@observable showMinimap: boolean = false;
@observable horzZoom: number = 0.5;
@observable mouseInsideBounds: boolean = false;
@observable renderingComplete = true;
private controlsHandlers: IOncoprintControlsHandlers;
private controlsState: IOncoprintControlsState;
@observable.ref public oncoprint: OncoprintJS | undefined = undefined;
constructor(props: IOncoprinterProps) {
super(props);
makeObservable(this);
(window as any).oncoprinter = this;
const self = this;
this.controlsHandlers = this.buildControlsHandlers();
this.controlsState = observable({
get showUnalteredColumns() {
return self.props.store.showUnalteredColumns;
},
get showWhitespaceBetweenColumns() {
return self.showWhitespaceBetweenColumns;
},
get showClinicalTrackLegends() {
return self.showClinicalTrackLegends;
},
get showMinimap() {
return self.showMinimap;
},
get sortByMutationType() {
return self.sortByMutationType;
},
get sortByCaseListDisabled() {
return !self.props.store.inputSampleIdOrder;
},
get distinguishMutationType() {
return self.distinguishMutationType;
},
get distinguishGermlineMutations() {
return self.distinguishGermlineMutations;
},
get distinguishDrivers() {
return self.distinguishDrivers;
},
get annotateDriversOncoKb() {
return self.props.store.driverAnnotationSettings.oncoKb;
},
get annotateDriversOncoKbDisabled() {
return !getServerConfig().show_oncokb;
},
get annotateDriversOncoKbError() {
return self.props.store.didOncoKbFail;
},
get annotateDriversCBioPortal() {
return self.props.store.driverAnnotationSettings
.cbioportalCount;
},
get hidePutativePassengers() {
return !self.props.store.driverAnnotationSettings.includeVUS;
},
get hideGermlineMutations() {
return self.props.store.hideGermlineMutations;
},
get annotateCBioPortalInputValue() {
return (
self.props.store.driverAnnotationSettings
.cbioportalCountThreshold + ''
);
},
get sortByDrivers() {
return self.sortByDrivers;
},
get horzZoom() {
if (isNaN(self.horzZoom)) {
return 1;
} else {
return self.horzZoom;
}
},
get annotateDriversHotspots() {
return self.props.store.driverAnnotationSettings.hotspots;
},
get annotateDriversHotspotsDisabled() {
return !getServerConfig().show_hotspot;
},
get annotateCustomDriverBinary() {
return self.props.store.driverAnnotationSettings.customBinary;
},
get customDriverAnnotationBinaryMenuLabel() {
if (self.props.store.existCustomDrivers) {
return 'User-specified drivers';
} else {
return undefined;
}
},
get isLoggedIn() {
// do nothing in oncoprinter mode:
return false;
},
get isClinicalTrackConfigDirty() {
// do nothing in oncoprinter mode:
return false;
},
get isSessionServiceEnabled() {
// do nothing in oncoprinter mode:
return false;
},
});
}
@computed get distinguishDrivers() {
return this.props.store.driverAnnotationSettings.driversAnnotated;
}
@autobind
onMouseEnter() {
this.mouseInsideBounds = true;
}
@autobind
onMouseLeave() {
this.mouseInsideBounds = false;
}
private buildControlsHandlers() {
return {
onSelectShowUnalteredColumns: (show: boolean) => {
this.props.store.showUnalteredColumns = show;
},
onSelectShowWhitespaceBetweenColumns: (show: boolean) => {
this.showWhitespaceBetweenColumns = show;
},
onSelectShowClinicalTrackLegends: (show: boolean) => {
this.showClinicalTrackLegends = show;
},
onSelectShowMinimap: (show: boolean) => {
this.showMinimap = show;
},
onSelectDistinguishMutationType: (s: boolean) => {
this.distinguishMutationType = s;
},
onSelectDistinguishGermlineMutations: (s: boolean) => {
this.distinguishGermlineMutations = s;
},
onSelectDistinguishDrivers: action((s: boolean) => {
if (!s) {
this.props.store.driverAnnotationSettings.oncoKb = false;
this.props.store.driverAnnotationSettings.cbioportalCount = false;
this.props.store.driverAnnotationSettings.customBinary = false;
this.props.store.driverAnnotationSettings.includeVUS = true;
} else {
if (
!this.controlsState.annotateDriversOncoKbDisabled &&
!this.controlsState.annotateDriversOncoKbError
) {
this.props.store.driverAnnotationSettings.oncoKb = true;
}
this.props.store.driverAnnotationSettings.cbioportalCount = true;
this.props.store.driverAnnotationSettings.customBinary = true;
}
}),
onSelectAnnotateOncoKb: action((s: boolean) => {
this.props.store.driverAnnotationSettings.oncoKb = s;
}),
onSelectCustomDriverAnnotationBinary: action((s: boolean) => {
this.props.store.driverAnnotationSettings.customBinary = s;
}),
onSelectHideVUS: (s: boolean) => {
this.props.store.driverAnnotationSettings.includeVUS = !s;
},
onSelectHideGermlineMutations: (s: boolean) => {
this.props.store.hideGermlineMutations = s;
},
onSelectSortByMutationType: (s: boolean) => {
this.sortByMutationType = s;
},
onSelectSortByDrivers: (sort: boolean) => {
this.sortByDrivers = sort;
},
onClickDownload: (type: string) => {
switch (type) {
case 'pdf':
svgToPdfDownload(
'oncoprint.pdf',
this.oncoprint!.toSVG(false)
);
// if (!pdfDownload("oncoprint.pdf", this.oncoprint.toSVG(true))) {
// alert("Oncoprint too big to download as PDF - please download as SVG.");
// }
break;
case 'png':
const img = this.oncoprint!.toCanvas(
(canvas, truncated) => {
canvas.toBlob(blob => {
if (truncated) {
alert(
`Oncoprint too large - PNG truncated to ${canvas.getAttribute(
'width'
)}x${canvas.getAttribute('height')}`
);
}
fileDownload(blob, 'oncoprint.png');
});
},
2
);
break;
case 'svg':
fileDownload(
new XMLSerializer().serializeToString(
this.oncoprint!.toSVG(false)
),
'oncoprint.svg'
);
break;
case 'order':
const capitalizedColumnMode = 'Sample';
let file = `${capitalizedColumnMode} order in the Oncoprint is:\n`;
const caseIds = this.oncoprint!.getIdOrder();
for (const caseId of caseIds) {
file += `${caseId}\n`;
}
fileDownload(file, `OncoPrintSamples.txt`);
break;
}
},
onSetHorzZoom: (z: number) => {
this.oncoprint!.setHorzZoomCentered(z);
},
onClickZoomIn: () => {
this.oncoprint!.setHorzZoomCentered(
this.oncoprint!.getHorzZoom() / 0.7
);
},
onClickZoomOut: () => {
this.oncoprint!.setHorzZoomCentered(
this.oncoprint!.getHorzZoom() * 0.7
);
},
onClickNGCHM: () => {}, // do nothing in oncoprinter mode
};
}
@action
private initializeOncoprint() {
onMobxPromise(
this.props.store.alteredSampleIds,
(alteredUids: string[]) => {
this.oncoprint!.setHorzZoomToFit(alteredUids);
}
);
this.oncoprint!.onHorzZoom(z => (this.horzZoom = z));
this.horzZoom = this.oncoprint!.getHorzZoom();
}
@action.bound
private oncoprintRef(oncoprint: OncoprintJS) {
this.oncoprint = oncoprint;
this.initializeOncoprint();
}
@action.bound
private onMinimapClose() {
this.showMinimap = false;
}
@action.bound
private onSuppressRendering() {
this.renderingComplete = false;
}
@action.bound
private onReleaseRendering() {
this.renderingComplete = true;
}
@computed get sortConfig() {
return {
sortByMutationType: this.sortByMutationType,
sortByDrivers: this.sortByDrivers,
order: this.props.store.inputSampleIdOrder,
};
}
@computed get alterationInfo() {
if (this.props.store.alteredSampleIds.isComplete) {
const numSamples = this.props.store.sampleIds.length;
const alteredSamples = this.props.store.alteredSampleIds.result
.length;
return (
{`Altered in ${alteredSamples} (${percentAltered(
alteredSamples,
numSamples
)}) of ${numSamples} samples.`}
);
} else {
return null;
}
}
@computed get isLoading() {
// todo: use mobxview
return (
this.props.store.geneticTracks.isPending ||
this.props.store.alteredSampleIds.isPending ||
this.props.store.unalteredSampleIds.isPending
);
}
@computed get isHidden() {
return this.isLoading || !this.renderingComplete;
}
@autobind
private getControls() {
if (this.oncoprint && !this.oncoprint.webgl_unavailable) {
return (