Skip to content

Commit

Permalink
Merge pull request #356 from hammerlab/options-menu
Browse files Browse the repository at this point in the history
Add an options menu
  • Loading branch information
danvk committed Nov 10, 2015
2 parents 982602f + 355b9fe commit 2a198b7
Show file tree
Hide file tree
Showing 11 changed files with 226 additions and 3 deletions.
Binary file modified screenshots/pileup_blocks-mismatch.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified screenshots/pileup_deletions.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified screenshots/pileup_gene-utf.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified screenshots/pileup_inserts.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified screenshots/pileup_loose.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified screenshots/pileup_small-letters-mismatch.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified screenshots/pileup_wide.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
71 changes: 71 additions & 0 deletions src/main/Menu.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
/**
* A generic menu, intended to be used for both toggling options and invoking commands.
*
* Usage:
*
* var items = [
* {key: 'a', label: 'Item A', checked: true},
* {key: 'b', label: 'Item B'},
* '-',
* {key: 'random', label: 'Pick Random'}
* ];
* <Menu header="Header" items={items} onSelect={selectHandler} />
*
* @flow
*/

'use strict';

var React = require('react');

type MenuItem = {
key: string;
label: string;
checked?: boolean;
};

type Props = {
header: string;
items: Array<MenuItem|'-'>;
onSelect: (key: string) => void;
};

class Menu extends React.Component<{}, Props, void> {
props: Props;

clickHandler(idx: number, e: SyntheticMouseEvent) {
e.preventDefault();
var item = this.props.items[idx];
if (typeof(item) == 'string') return; // for flow
this.props.onSelect(item.key);
}

render(): any {
var makeHandler = i => this.clickHandler.bind(this, i);
var els = [];
if (this.props.header) {
els.push(<div key='header' className='menu-header'>{this.props.header}</div>);
}
els = els.concat(this.props.items.map((item, i) => {
if (typeof(item) === 'string') { // would prefer "== '-'", see flow#1066
return <div key={i} className='menu-separator' />;
} else {
var checkClass = 'check' + (item.checked ? ' checked' : '');
return (
<div key={i} className='menu-item' onClick={makeHandler(i)}>
<span className={checkClass}></span>
<span className='menu-item-label'>{item.label}</span>
</div>
);
}
}));

return (
<div className='menu'>
{els}
</div>
);
}
}

module.exports = Menu;
47 changes: 46 additions & 1 deletion src/main/PileupTrack.js
Original file line number Diff line number Diff line change
Expand Up @@ -212,6 +212,8 @@ class PileupTrack extends React.Component {
cache: PileupCache;
tiles: TiledCanvas;
static defaultOptions: { viewAsPairs: boolean };
static getOptionsMenu: (options: Object) => any;
static handleSelectOption: (key: string, oldOptions: Object) => Object;

constructor(props: VizProps) {
super(props);
Expand Down Expand Up @@ -293,12 +295,33 @@ class PileupTrack extends React.Component {
}

componentDidUpdate(prevProps: any, prevState: any) {
var shouldUpdate = false;
if (this.props.options != prevProps.options) {
this.handleOptionsChange(prevProps.options);
shouldUpdate = true;
}

if (!shallowEquals(this.props, prevProps) ||
!shallowEquals(this.state, prevState)) {
!shallowEquals(this.state, prevState) ||
shouldUpdate) {
this.updateVisualization();
}
}

handleOptionsChange(oldOpts: Object) {
this.tiles.invalidateAll();

if (oldOpts.viewAsPairs != this.props.options.viewAsPairs) {
this.cache = new PileupCache(this.props.referenceSource, this.props.options.viewAsPairs);
this.tiles = new PileupTileCache(this.cache);
this.updateReads(ContigInterval.fromGenomeRange(this.props.range));
}

if (oldOpts.sort != this.props.options.sort) {
this.handleSort();
}
}

// Load new reads into the visualization cache.
updateReads(range: ContigInterval<string>) {
var source = (this.props.source : AlignmentDataSource);
Expand Down Expand Up @@ -398,5 +421,27 @@ PileupTrack.defaultOptions = {
viewAsPairs: false
};

PileupTrack.getOptionsMenu = function(options: Object): any {
return [
{key: 'view-pairs', label: 'View as pairs', checked: options.viewAsPairs},
'-',
{key: 'sort', label: 'Sort alignments'}
];
};

var messageId = 1;

PileupTrack.handleSelectOption = function(key: string, oldOptions: Object): Object {
var opts = _.clone(oldOptions);
if (key == 'view-pairs') {
opts.viewAsPairs = !opts.viewAsPairs;
return opts;
} else if (key == 'sort') {
opts.sort = (messageId++);
return opts;
}
return oldOptions; // no change
};


module.exports = PileupTrack;
63 changes: 61 additions & 2 deletions src/main/Root.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import type {VisualizedTrack, VizWithOptions} from './types';

var React = require('react'),
Controls = require('./Controls'),
Menu = require('./Menu'),
VisualizationWrapper = require('./VisualizationWrapper');

type Props = {
Expand All @@ -22,13 +23,15 @@ class Root extends React.Component {
state: {
contigList: string[];
range: ?GenomeRange;
settingsMenuKey: ?string;
};

constructor(props: Object) {
super(props);
this.state = {
contigList: this.props.referenceSource.contigList(),
range: null
range: null,
settingsMenuKey: null
};
}

Expand Down Expand Up @@ -57,6 +60,26 @@ class Root extends React.Component {
}).done();
}

toggleSettingsMenu(key: string, e: SyntheticEvent) {
if (this.state.settingsMenuKey == key) {
this.setState({settingsMenuKey: null});
} else {
this.setState({settingsMenuKey: key});
}
}

handleSelectOption(trackKey: string, optionKey: string) {
this.setState({settingsMenuKey: null});

var viz = this.props.tracks[Number(trackKey)].visualization;
var oldOpts = viz.options;
var newOpts = viz.component.handleSelectOption(optionKey, oldOpts);
viz.options = newOpts;
if (newOpts != oldOpts) {
this.forceUpdate();
}
}

makeDivForTrack(key: string, track: VisualizedTrack): React.Element {
var trackEl = (
<VisualizationWrapper visualization={track.visualization}
Expand All @@ -65,13 +88,49 @@ class Root extends React.Component {
source={track.source}
referenceSource={this.props.referenceSource}
/>);

var trackName = track.track.name || '(track name)';

var gearIcon = null,
settingsMenu = null;
if (track.visualization.component.getOptionsMenu) {
gearIcon = (
<span ref={'gear-' + key}
className='gear'
onClick={this.toggleSettingsMenu.bind(this, key)}>
</span>
);
}

if (this.state.settingsMenuKey == key) {
var gear = this.refs['gear-' + key],
gearX = gear.offsetLeft,
gearW = gear.offsetWidth,
gearY = gear.offsetTop;

var menuStyle = {
position: 'absolute',
left: (gearX + gearW) + 'px',
top: gearY + 'px'
};
var items = track.visualization.component.getOptionsMenu(track.visualization.options);
settingsMenu = (
<div className='menu-container' style={menuStyle}>
<Menu header={trackName} items={items} onSelect={this.handleSelectOption.bind(this, key)} />
</div>
);
}

var className = ['track', track.visualization.component.displayName || '', track.track.cssClass || ''].join(' ');

return (
<div key={key} className={className}>
<div className='track-label'>
<span>{track.track.name || '(track name)'}</span>
<span>{trackName}</span>
<br/>
{gearIcon}
{settingsMenu}
</div>
<div className='track-content'>
{trackEl}
Expand Down
48 changes: 48 additions & 0 deletions style/pileup.css
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,9 @@
.pileup-root > .controls {
flex: 0 0 30px; /* fixed height */
}
.pileup-root > .controls > .track-content {
overflow: visible;
}
.pileup-root > .controls form.controls {
margin-bottom: 0; /* overrides browser default */
}
Expand All @@ -66,6 +69,51 @@
.pileup-root > .controls option {
font-size: 0.9em;
}

.gear {
margin-left: 0.5em;
font-size: 2em;
color: #666;
}
.gear:hover {
color: black;
}
.menu-container {
z-index: 1;
width: 250px; /* not really 250px, but clears parent constraint */
}
.menu {
border: 1px solid black;
border-radius: 2px;
display: table;
background: white;
font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif;
text-align: left;
}
.menu-header {
font-weight: bold;
border-bottom: 1px solid #777;
padding: 2px 4px;
}
.menu-item {
padding: 4px;
}
.menu-separator {
border-top: 1px solid #777;
height: 0px;
}
.menu-item:hover {
background: lightblue;
}

.check {
display: inline-block;
width: 1em;
}
.check.checked:before {
content: '✓';
}

/* reference track */
.pileup-root > .reference {
flex: 0 0 20px; /* fixed height */
Expand Down

0 comments on commit 2a198b7

Please sign in to comment.