UNPKG

molstar

Version:

A comprehensive macromolecular library.

282 lines 14.1 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.Sequence = void 0; var tslib_1 = require("tslib"); var jsx_runtime_1 = require("react/jsx-runtime"); /** * Copyright (c) 2018-2020 mol* contributors, licensed under MIT, See LICENSE file for more info. * * @author Alexander Rose <alexander.rose@weirdbyte.de> * @author David Sehnal <david.sehnal@gmail.com> */ var React = (0, tslib_1.__importStar)(require("react")); var base_1 = require("../base"); var input_observer_1 = require("../../mol-util/input/input-observer"); var structure_1 = require("../../mol-model/structure"); var rxjs_1 = require("rxjs"); var operators_1 = require("rxjs/operators"); var int_1 = require("../../mol-data/int"); var representation_1 = require("../../mol-repr/representation"); /** Note, if this is changed, the CSS for `msp-sequence-number` needs adjustment too */ var MaxSequenceNumberSize = 5; // TODO: this is somewhat inefficient and should be done using a canvas. var Sequence = /** @class */ (function (_super) { (0, tslib_1.__extends)(Sequence, _super); function Sequence() { var _this = _super !== null && _super.apply(this, arguments) || this; _this.parentDiv = React.createRef(); _this.lastMouseOverSeqIdx = -1; _this.highlightQueue = new rxjs_1.Subject(); _this.lociHighlightProvider = function (loci, action) { var changed = _this.props.sequenceWrapper.markResidue(loci.loci, action); if (changed) _this.updateMarker(); }; _this.lociSelectionProvider = function (loci, action) { var changed = _this.props.sequenceWrapper.markResidue(loci.loci, action); if (changed) _this.updateMarker(); }; _this.contextMenu = function (e) { e.preventDefault(); }; _this.mouseDownLoci = undefined; _this.mouseDown = function (e) { e.stopPropagation(); var seqIdx = _this.getSeqIdx(e); var loci = _this.getLoci(seqIdx); var buttons = (0, input_observer_1.getButtons)(e.nativeEvent); var button = (0, input_observer_1.getButton)(e.nativeEvent); var modifiers = (0, input_observer_1.getModifiers)(e.nativeEvent); _this.click(loci, buttons, button, modifiers); _this.mouseDownLoci = loci; }; _this.mouseUp = function (e) { e.stopPropagation(); // ignore mouse-up events without a bound loci if (_this.mouseDownLoci === undefined) return; var seqIdx = _this.getSeqIdx(e); var loci = _this.getLoci(seqIdx); if (loci && !structure_1.StructureElement.Loci.areEqual(_this.mouseDownLoci, loci)) { var buttons = (0, input_observer_1.getButtons)(e.nativeEvent); var button = (0, input_observer_1.getButton)(e.nativeEvent); var modifiers = (0, input_observer_1.getModifiers)(e.nativeEvent); var ref = _this.mouseDownLoci.elements[0]; var ext = loci.elements[0]; var min = Math.min(int_1.OrderedSet.min(ref.indices), int_1.OrderedSet.min(ext.indices)); var max = Math.max(int_1.OrderedSet.max(ref.indices), int_1.OrderedSet.max(ext.indices)); var range = structure_1.StructureElement.Loci(loci.structure, [{ unit: ref.unit, indices: int_1.OrderedSet.ofRange(min, max) }]); _this.click(structure_1.StructureElement.Loci.subtract(range, _this.mouseDownLoci), buttons, button, modifiers); } _this.mouseDownLoci = undefined; }; _this.location = structure_1.StructureElement.Location.create(void 0); _this.mouseMove = function (e) { e.stopPropagation(); var buttons = (0, input_observer_1.getButtons)(e.nativeEvent); var button = (0, input_observer_1.getButton)(e.nativeEvent); var modifiers = (0, input_observer_1.getModifiers)(e.nativeEvent); var el = e.target; if (!el || !el.getAttribute) { if (_this.lastMouseOverSeqIdx === -1) return; _this.lastMouseOverSeqIdx = -1; _this.highlightQueue.next({ seqIdx: -1, buttons: buttons, button: button, modifiers: modifiers }); return; } var seqIdx = el.hasAttribute('data-seqid') ? +el.getAttribute('data-seqid') : -1; if (_this.lastMouseOverSeqIdx === seqIdx) { return; } else { _this.lastMouseOverSeqIdx = seqIdx; if (_this.mouseDownLoci !== undefined) { var loci = _this.getLoci(seqIdx); _this.hover(loci, 0 /* None */, 0 /* None */, (0, tslib_1.__assign)((0, tslib_1.__assign)({}, modifiers), { shift: true })); } else { _this.highlightQueue.next({ seqIdx: seqIdx, buttons: buttons, button: button, modifiers: modifiers }); } } }; _this.mouseLeave = function (e) { e.stopPropagation(); _this.mouseDownLoci = undefined; if (_this.lastMouseOverSeqIdx === -1) return; _this.lastMouseOverSeqIdx = -1; var buttons = (0, input_observer_1.getButtons)(e.nativeEvent); var button = (0, input_observer_1.getButton)(e.nativeEvent); var modifiers = (0, input_observer_1.getModifiers)(e.nativeEvent); _this.highlightQueue.next({ seqIdx: -1, buttons: buttons, button: button, modifiers: modifiers }); }; return _this; } Object.defineProperty(Sequence.prototype, "sequenceNumberPeriod", { get: function () { if (this.props.sequenceNumberPeriod !== undefined) { return this.props.sequenceNumberPeriod; } if (this.props.sequenceWrapper.length > 10) return 10; var lastSeqNum = this.getSequenceNumber(this.props.sequenceWrapper.length - 1); if (lastSeqNum.length > 1) return 5; return 1; }, enumerable: false, configurable: true }); Sequence.prototype.componentDidMount = function () { var _this = this; this.plugin.managers.interactivity.lociHighlights.addProvider(this.lociHighlightProvider); this.plugin.managers.interactivity.lociSelects.addProvider(this.lociSelectionProvider); this.subscribe((0, operators_1.debounceTime)(15)(this.highlightQueue), function (e) { var loci = _this.getLoci(e.seqIdx < 0 ? void 0 : e.seqIdx); _this.hover(loci, e.buttons, e.button, e.modifiers); }); // this.updateMarker() }; Sequence.prototype.componentWillUnmount = function () { _super.prototype.componentWillUnmount.call(this); this.plugin.managers.interactivity.lociHighlights.removeProvider(this.lociHighlightProvider); this.plugin.managers.interactivity.lociSelects.removeProvider(this.lociSelectionProvider); }; Sequence.prototype.getLoci = function (seqIdx) { if (seqIdx !== undefined) { var loci = this.props.sequenceWrapper.getLoci(seqIdx); if (!structure_1.StructureElement.Loci.isEmpty(loci)) return loci; } }; Sequence.prototype.getSeqIdx = function (e) { var seqIdx = undefined; var el = e.target; if (el && el.getAttribute) { seqIdx = el.hasAttribute('data-seqid') ? +el.getAttribute('data-seqid') : undefined; } return seqIdx; }; Sequence.prototype.hover = function (loci, buttons, button, modifiers) { var ev = { current: representation_1.Representation.Loci.Empty, buttons: buttons, button: button, modifiers: modifiers }; if (loci !== undefined && !structure_1.StructureElement.Loci.isEmpty(loci)) { ev.current = { loci: loci }; } this.plugin.behaviors.interaction.hover.next(ev); }; Sequence.prototype.click = function (loci, buttons, button, modifiers) { var ev = { current: representation_1.Representation.Loci.Empty, buttons: buttons, button: button, modifiers: modifiers }; if (loci !== undefined && !structure_1.StructureElement.Loci.isEmpty(loci)) { ev.current = { loci: loci }; } this.plugin.behaviors.interaction.click.next(ev); }; Sequence.prototype.getBackgroundColor = function (marker) { // TODO: make marker color configurable if (typeof marker === 'undefined') console.error('unexpected marker value'); return marker === 0 ? '' : marker % 2 === 0 ? 'rgb(51, 255, 25)' // selected : 'rgb(255, 102, 153)'; // highlighted }; Sequence.prototype.getResidueClass = function (seqIdx, label) { return label.length > 1 ? this.props.sequenceWrapper.residueClass(seqIdx) + (seqIdx === 0 ? ' msp-sequence-residue-long-begin' : ' msp-sequence-residue-long') : this.props.sequenceWrapper.residueClass(seqIdx); }; Sequence.prototype.residue = function (seqIdx, label, marker) { return (0, jsx_runtime_1.jsx)("span", (0, tslib_1.__assign)({ "data-seqid": seqIdx, style: { backgroundColor: this.getBackgroundColor(marker) }, className: this.getResidueClass(seqIdx, label) }, { children: "\u200B" + label + "\u200B" }), seqIdx); }; Sequence.prototype.getSequenceNumberClass = function (seqIdx, seqNum, label) { var classList = ['msp-sequence-number']; if (seqNum.startsWith('-')) { if (label.length > 1 && seqIdx > 0) classList.push('msp-sequence-number-long-negative'); else classList.push('msp-sequence-number-negative'); } else { if (label.length > 1 && seqIdx > 0) classList.push('msp-sequence-number-long'); } return classList.join(' '); }; Sequence.prototype.getSequenceNumber = function (seqIdx) { var seqNum = ''; var loci = this.props.sequenceWrapper.getLoci(seqIdx); var l = structure_1.StructureElement.Loci.getFirstLocation(loci, this.location); if (l) { if (structure_1.Unit.isAtomic(l.unit)) { var seqId = structure_1.StructureProperties.residue.auth_seq_id(l); var insCode = structure_1.StructureProperties.residue.pdbx_PDB_ins_code(l); seqNum = "" + seqId + (insCode ? insCode : ''); } else if (structure_1.Unit.isCoarse(l.unit)) { seqNum = "" + (seqIdx + 1); } } return seqNum; }; Sequence.prototype.padSeqNum = function (n) { if (n.length < MaxSequenceNumberSize) return n + new Array(MaxSequenceNumberSize - n.length + 1).join('\u00A0'); return n; }; Sequence.prototype.getSequenceNumberSpan = function (seqIdx, label) { var seqNum = this.getSequenceNumber(seqIdx); return (0, jsx_runtime_1.jsx)("span", (0, tslib_1.__assign)({ className: this.getSequenceNumberClass(seqIdx, seqNum, label) }, { children: this.padSeqNum(seqNum) }), "marker-" + seqIdx); }; Sequence.prototype.updateMarker = function () { if (!this.parentDiv.current) return; var xs = this.parentDiv.current.children; var markerArray = this.props.sequenceWrapper.markerArray; var hasNumbers = !this.props.hideSequenceNumbers, period = this.sequenceNumberPeriod; // let first: HTMLSpanElement | undefined; var o = 0; for (var i = 0, il = markerArray.length; i < il; i++) { if (hasNumbers && i % period === 0 && i < il) o++; // o + 1 to account for help icon var span = xs[o]; if (!span) return; o++; // if (!first && markerArray[i] > 0) { // first = span; // } var backgroundColor = this.getBackgroundColor(markerArray[i]); if (span.style.backgroundColor !== backgroundColor) span.style.backgroundColor = backgroundColor; } // if (first) { // first.scrollIntoView({ block: 'nearest' }); // } }; Sequence.prototype.render = function () { var sw = this.props.sequenceWrapper; var elems = []; var hasNumbers = !this.props.hideSequenceNumbers, period = this.sequenceNumberPeriod; for (var i = 0, il = sw.length; i < il; ++i) { var label = sw.residueLabel(i); // add sequence number before name so the html element do not get separated by a line-break if (hasNumbers && i % period === 0 && i < il) { elems[elems.length] = this.getSequenceNumberSpan(i, label); } elems[elems.length] = this.residue(i, label, sw.markerArray[i]); } // calling .updateMarker here is neccesary to ensure existing // residue spans are updated as react won't update them this.updateMarker(); return (0, jsx_runtime_1.jsx)("div", (0, tslib_1.__assign)({ className: 'msp-sequence-wrapper', onContextMenu: this.contextMenu, onMouseDown: this.mouseDown, onMouseUp: this.mouseUp, onMouseMove: this.mouseMove, onMouseLeave: this.mouseLeave, ref: this.parentDiv }, { children: elems }), void 0); }; return Sequence; }(base_1.PluginUIComponent)); exports.Sequence = Sequence; //# sourceMappingURL=sequence.js.map