molstar
Version:
A comprehensive macromolecular library.
282 lines • 14.1 kB
JavaScript
"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