UNPKG

react-sequence-viewer

Version:

A React wrapper around the BioJS sequence-viewer component

183 lines (138 loc) 7.26 kB
var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; function _objectWithoutProperties(obj, keys) { var target = {}; for (var i in obj) { if (keys.indexOf(i) >= 0) continue; if (!Object.prototype.hasOwnProperty.call(obj, i)) continue; target[i] = obj[i]; } return target; } function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; } function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } import React, { Component } from 'react'; import PropTypes from 'prop-types'; import { v4 } from 'uuid'; import Sequence from 'sequence-viewer'; var ReactSequenceViewer = function (_Component) { _inherits(ReactSequenceViewer, _Component); function ReactSequenceViewer(props) { _classCallCheck(this, ReactSequenceViewer); var _this = _possibleConstructorReturn(this, _Component.call(this, props)); _this.handleChange = _this.handleChange.bind(_this); _this.handleRef = _this.handleRef.bind(_this); if (props.selection && props.selection.length > 0 && props.coverage && props.coverage.length > 0) { console.warn("The selection and coverage options are not compatible with each other."); } // Initialize the sequence-viewer object. _this._seqObj = new Sequence(_this.props.sequence); _this._div = null; return _this; } // Function to call the render function of sequence-viewer. // You can override existing props by passing an object with key value // pairs to override existing props. // e.g. // callRender({toolbar: false}) // would override the existing toolbar setting. ReactSequenceViewer.prototype.callRender = function callRender() { var newProps = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; var _props = this.props, selection = _props.selection, props = _objectWithoutProperties(_props, ['selection']); // Read in div from private variable. var div = this._div; //Render div if it is not null. if (div !== null) { var _seqObj; this._seqObj.render('#' + div.id, _extends({}, props, newProps)); if (this.props.coverage.length > 0) this._seqObj.coverage(this.props.coverage); if (this.props.legend.length > 0) this._seqObj.addLegend(this.props.legend); if (selection.length > 0) (_seqObj = this._seqObj).selection.apply(_seqObj, selection); } }; // When the component mounts, add a change listener to the document // and call render. We attach the change listener here because // jQuery events don't bubble up through React due to its synthetic event // handling. Thus, when a user toggles the charsPerLine drop down menu. // the event is handled by jQuery, but not seen by React when the // listener is attached at the component div level. // Attaching it to the document seems to work. ReactSequenceViewer.prototype.componentDidMount = function componentDidMount() { document.addEventListener('change', this.handleChange); this.callRender(); this._seqObj.onSubpartSelected(this.props.onSubpartSelected); this._seqObj.onMouseSelection(this.props.onMouseSelection); }; // Update the sequence-viewer object if we get a new DNA sequence. ReactSequenceViewer.prototype.componentWillReceiveProps = function componentWillReceiveProps(nextProps) { if (this.props.sequence !== nextProps.sequence) { this._seqObj = new Sequence(nextProps.sequence); } }; // Re-render if the component has updated. ReactSequenceViewer.prototype.componentDidUpdate = function componentDidUpdate(prevProps, prevState) { if (this.props !== prevProps) { this.callRender(); } }; // Remove the event listener when the component is unmounted. ReactSequenceViewer.prototype.componentWillUnmount = function componentWillUnmount() { document.removeEventListener('change', this.handleChange); }; // Function called when the user changes the charsPerLine setting via the toolbar. ReactSequenceViewer.prototype.handleChange = function handleChange(e) { var elem = e.target; // Check that the event was triggered by the right <select> button. if ((" " + elem.className + " ").indexOf(" " + this.props.seqLenClass + " ") > -1) { // Call render and override the charsPerLine setting with whatever the user specified. this.callRender({ charsPerLine: elem.value }); } }; ReactSequenceViewer.prototype.handleRef = function handleRef(div) { this._div = div; }; // Render a div with the sequence-viwer widget in it. ReactSequenceViewer.prototype.render = function render() { var _props2 = this.props, id = _props2.id, sequence = _props2.sequence, className = _props2.className; // Create the container div and store a reference to it once it is mounted // in the DOM. The componentDidMount function above will then get called // and render the widget. return React.createElement('div', { className: className, id: this.props.id, ref: this.handleRef }); }; return ReactSequenceViewer; }(Component); export { ReactSequenceViewer as default }; ReactSequenceViewer.propTypes = process.env.NODE_ENV !== "production" ? { id: PropTypes.string, sequence: PropTypes.string.isRequired, className: PropTypes.string, selection: PropTypes.arrayOf(function (arr, key, compName, location, propFullName) { if (arr.length !== 3) { return new Error('Invalid prop `selection` supplied to `' + compName + '`. Validation failed.'); } }), coverage: PropTypes.arrayOf(PropTypes.shape({ start: PropTypes.number.isRequired, end: PropTypes.number.isRequired, color: PropTypes.string, bgcolor: PropTypes.string, underscore: PropTypes.bool, tooltip: PropTypes.string, onclick: PropTypes.func })), legend: PropTypes.arrayOf(PropTypes.shape({ name: PropTypes.string, color: PropTypes.string, underscore: PropTypes.bool })), seqLenClass: PropTypes.string, onMouseSelection: PropTypes.func, onSubpartSelected: PropTypes.func } : {}; ReactSequenceViewer.defaultProps = { id: v4(), coverage: [], legend: [], selection: [], seqLenClass: "CPLChoice", onMouseSelection: function onMouseSelection(elem) {}, onSubpartSelected: function onSubpartSelected(elem) {}, className: '' };