From b030d0b7dd4b54f2e2139a1382addc1599a5b597 Mon Sep 17 00:00:00 2001 From: Jake Coble Date: Thu, 17 Dec 2015 15:51:24 -0800 Subject: [PATCH 1/4] convert HighlightLayer to ES6 --- app/HighlightLayer.js | 46 ++++++++++++++++++++++++++++--------------- app/RowItem.js | 5 +++-- 2 files changed, 33 insertions(+), 18 deletions(-) diff --git a/app/HighlightLayer.js b/app/HighlightLayer.js index a870d1c4a..68900d620 100644 --- a/app/HighlightLayer.js +++ b/app/HighlightLayer.js @@ -1,9 +1,11 @@ +import React, {PropTypes} from 'react'; +import Caret from './Caret'; +import PureRenderMixin from 'react-addons-pure-render-mixin'; +import { propTypes } from './react-props-decorators.js'; + var getXStartAndWidthOfRowAnnotation = require('./getXStartAndWidthOfRowAnnotation'); var assign = require('lodash/object/assign'); -let React = require('react'); -var Caret = require('./Caret'); let getOverlapsOfPotentiallyCircularRanges = require('ve-range-utils/getOverlapsOfPotentiallyCircularRanges'); -let PureRenderMixin = require('react-addons-pure-render-mixin'); var highlightLayerStyle = { height: "98%", @@ -12,17 +14,28 @@ var highlightLayerStyle = { opacity: ".3", }; -let HighlightLayer = React.createClass({ - mixins: [PureRenderMixin], - propTypes: { - charWidth: React.PropTypes.number.isRequired, - bpsPerRow: React.PropTypes.number.isRequired, - color: React.PropTypes.string, - row: React.PropTypes.object.isRequired, - sequenceLength: React.PropTypes.number.isRequired, - selectionLayer: React.PropTypes.object.isRequired, - }, - render: function() { +function mixin(target, source) { + target = target.prototype; + + Object.getOwnPropertyNames(source).forEach((name) => { + let sourceProp = Object.getOwnPropertyDescriptor(source, name); + + if (name !== "constructor") { + Object.defineProperty(target, name, sourceProp); + } + }); +} + +@propTypes({ + charWidth: PropTypes.number.isRequired, + bpsPerRow: PropTypes.number.isRequired, + color: PropTypes.string, + row: PropTypes.object.isRequired, + sequenceLength: PropTypes.number.isRequired, + selectionLayer: PropTypes.object.isRequired, +}) +export default class HighlightLayer extends React.Component { + render() { var { charWidth, bpsPerRow, @@ -78,7 +91,8 @@ let HighlightLayer = React.createClass({ return null; } } -}); +} +mixin(HighlightLayer, PureRenderMixin); -module.exports = HighlightLayer; \ No newline at end of file +module.exports = HighlightLayer; diff --git a/app/RowItem.js b/app/RowItem.js index ef9c542b4..d4a61ea5f 100644 --- a/app/RowItem.js +++ b/app/RowItem.js @@ -1,4 +1,6 @@ import React, {PropTypes} from 'react'; +import HighlightLayer from './HighlightLayer'; + var getComplementSequenceString = require('ve-sequence-utils/getComplementSequenceString'); var SequenceContainer = require('./SequenceContainer'); var AxisContainer = require('./AxisContainer'); @@ -7,7 +9,6 @@ var TranslationContainer = require('./TranslationContainer'); var FeatureContainer = require('./FeatureContainer'); var CutsiteLabelContainer = require('./CutsiteLabelContainer'); var CutsiteSnipsContainer = require('./CutsiteSnipsContainer'); -var HighlightLayer = require('./HighlightLayer'); var Caret = require('./Caret'); class RowItem extends React.Component { @@ -184,4 +185,4 @@ RowItem.propTypes = { row: PropTypes.object.isRequired }; -module.exports = RowItem; \ No newline at end of file +module.exports = RowItem; From 1b0d901b227bb7cbd988e7215e0bc3ba6aa04cb3 Mon Sep 17 00:00:00 2001 From: Jake Coble Date: Thu, 17 Dec 2015 20:58:26 -0800 Subject: [PATCH 2/4] move HighlightLayer styles to external stylesheet --- app/HighlightLayer.js | 24 +++++++++++------------- app/highlight-layer.css | 7 +++++++ 2 files changed, 18 insertions(+), 13 deletions(-) create mode 100644 app/highlight-layer.css diff --git a/app/HighlightLayer.js b/app/HighlightLayer.js index 68900d620..0aa1ef495 100644 --- a/app/HighlightLayer.js +++ b/app/HighlightLayer.js @@ -3,17 +3,11 @@ import Caret from './Caret'; import PureRenderMixin from 'react-addons-pure-render-mixin'; import { propTypes } from './react-props-decorators.js'; +import styles from './highlight-layer.css'; + var getXStartAndWidthOfRowAnnotation = require('./getXStartAndWidthOfRowAnnotation'); -var assign = require('lodash/object/assign'); let getOverlapsOfPotentiallyCircularRanges = require('ve-range-utils/getOverlapsOfPotentiallyCircularRanges'); -var highlightLayerStyle = { - height: "98%", - position: "absolute", - top: "0", - opacity: ".3", -}; - function mixin(target, source) { target = target.prototype; @@ -69,12 +63,16 @@ export default class HighlightLayer extends React.Component { var xStart = result.xStart; var width = result.width; - var style = assign({}, highlightLayerStyle, { + var style = { width: width, - left: xStart, - background: color ? color : 'blue' - }); - return (
); + left: xStart + }; + + if (color !== undefined) { + style.background = color; + } + + return (
); }); return (
Date: Fri, 18 Dec 2015 13:46:24 -0800 Subject: [PATCH 3/4] make HighlightLayer support multiple selection layers --- app/HighlightLayer.js | 107 +++++++++++++++++++++++------------------- app/RowItem.js | 4 +- 2 files changed, 62 insertions(+), 49 deletions(-) diff --git a/app/HighlightLayer.js b/app/HighlightLayer.js index 0aa1ef495..a80326d3a 100644 --- a/app/HighlightLayer.js +++ b/app/HighlightLayer.js @@ -26,7 +26,7 @@ function mixin(target, source) { color: PropTypes.string, row: PropTypes.object.isRequired, sequenceLength: PropTypes.number.isRequired, - selectionLayer: PropTypes.object.isRequired, + regions: PropTypes.array.isRequired, }) export default class HighlightLayer extends React.Component { render() { @@ -35,59 +35,72 @@ export default class HighlightLayer extends React.Component { bpsPerRow, row, sequenceLength, - selectionLayer, + regions, color } = this.props; - if (selectionLayer.selected) { - var startSelectionCursor; - var endSelectionCursor; - var overlaps = getOverlapsOfPotentiallyCircularRanges(selectionLayer, row, sequenceLength); - var selectionLayers = overlaps.map(function(overlap, index) { - if (overlap.start === selectionLayer.start) { - startSelectionCursor = () - } - if (overlap.end === selectionLayer.end) { - endSelectionCursor = () - } - var result = getXStartAndWidthOfRowAnnotation(overlap, bpsPerRow, charWidth); - var xStart = result.xStart; - var width = result.width; - - var style = { - width: width, - left: xStart - }; - - if (color !== undefined) { - style.background = color; - } - - return (
); - }); + + var selectionLayers; + + for (let i = 0; i < regions.length; i++) { + let selectionLayer = regions[i]; + + if (selectionLayer.selected) { + var startSelectionCursor; + var endSelectionCursor; + var overlaps = getOverlapsOfPotentiallyCircularRanges(selectionLayer, row, sequenceLength); + let layers = overlaps.map(function(overlap, index) { + if (overlap.start === selectionLayer.start) { + startSelectionCursor = (); + } + if (overlap.end === selectionLayer.end) { + endSelectionCursor = (); + } + var result = getXStartAndWidthOfRowAnnotation(overlap, bpsPerRow, charWidth); + var xStart = result.xStart; + var width = result.width; + + var style = { + width: width, + left: xStart + }; + + if (color !== undefined) { + style.background = color; + } + + return (
); + }); + + selectionLayers = selectionLayers || []; + selectionLayers.push(layers); + } + } + + if (selectionLayers !== undefined && selectionLayers.length > 0) { return (
- {selectionLayers} - {startSelectionCursor} - {endSelectionCursor} + //tnrtodo: add context menu here + event.preventDefault(); + event.stopPropagation(); + }}> + {selectionLayers} + {startSelectionCursor} + {endSelectionCursor}
); - } else { - return null; } + + return null; } } diff --git a/app/RowItem.js b/app/RowItem.js index d4a61ea5f..b0f1aca68 100644 --- a/app/RowItem.js +++ b/app/RowItem.js @@ -137,7 +137,7 @@ class RowItem extends React.Component { row={row} signals={signals} sequenceLength={sequenceLength} - selectionLayer={selectionLayer} + regions={[selectionLayer]} > {!selectionLayer.selected && From 5e810437671a38e415057a9738275d7e8b88d771 Mon Sep 17 00:00:00 2001 From: Jacob Coble Date: Tue, 22 Dec 2015 15:37:02 -0800 Subject: [PATCH 4/4] search the sequence --- app/RowItem.js | 11 ++++++++ app/RowView.js | 4 +++ app/ToolBar.js | 13 +++++++++ app/cerebral/actions/searchSequence.js | 32 ++++++++++++++++++++++ app/cerebral/actions/updateSearchLayers.js | 5 ++++ app/cerebral/signals.js | 4 +++ app/cerebral/state.js | 1 + 7 files changed, 70 insertions(+) create mode 100644 app/cerebral/actions/searchSequence.js create mode 100644 app/cerebral/actions/updateSearchLayers.js diff --git a/app/RowItem.js b/app/RowItem.js index b0f1aca68..4e0ef455f 100644 --- a/app/RowItem.js +++ b/app/RowItem.js @@ -16,6 +16,7 @@ class RowItem extends React.Component { var { charWidth, selectionLayer, + searchLayers, cutsiteLabelSelectionLayer, annotationHeight, tickSpacing, @@ -150,6 +151,16 @@ class RowItem extends React.Component { regions={[cutsiteLabelSelectionLayer]} > + + {!selectionLayer.selected && {toggleMenuItems} + + + + ); diff --git a/app/cerebral/actions/searchSequence.js b/app/cerebral/actions/searchSequence.js new file mode 100644 index 000000000..4b36176cb --- /dev/null +++ b/app/cerebral/actions/searchSequence.js @@ -0,0 +1,32 @@ +function searchSequence({ searchString }, tree, output) { + if (searchString.length === 0) { + output({ searchLayers: [] }); + return; + } + var sequence = tree.get(['sequenceData', 'sequence']); + + var layers = []; + var inResult = false; + var lastIndex = 0; + + for (let i = 0; i < sequence.length; i++) { + if (!inResult) { + lastIndex = i; + } + + if (sequence[i] === searchString[i - lastIndex]) { + inResult = true; + + if (i - lastIndex === searchString.length - 1) { + layers.push({ start: lastIndex, end: i, selected: true }); + inResult = false; + } + } else { + inResult = false; + } + } + + output({ searchLayers: layers }); +} + +module.exports = searchSequence; diff --git a/app/cerebral/actions/updateSearchLayers.js b/app/cerebral/actions/updateSearchLayers.js new file mode 100644 index 000000000..1b6f48a0c --- /dev/null +++ b/app/cerebral/actions/updateSearchLayers.js @@ -0,0 +1,5 @@ +function updateSearchLayers({ searchLayers }, tree, output) { + tree.set('searchLayers', searchLayers); +} + +module.exports = updateSearchLayers; diff --git a/app/cerebral/signals.js b/app/cerebral/signals.js index 8c629e2fe..141ad6264 100644 --- a/app/cerebral/signals.js +++ b/app/cerebral/signals.js @@ -82,6 +82,10 @@ export default function(controller, options) { resizeCircularView: [ a.resizeCircularView ], + searchSequence: [ + a.searchSequence, + a.updateSearchLayers + ], //tnr: NOT YET WORKING: //higher priority pasteSequenceString: [a.pasteSequenceString], diff --git a/app/cerebral/state.js b/app/cerebral/state.js index 16b8f0e42..557c5bd37 100644 --- a/app/cerebral/state.js +++ b/app/cerebral/state.js @@ -65,6 +65,7 @@ module.exports = { height: 500, width: 500 }, + searchLayers: [], selectionLayer: { start: -1, end: -1,