UNPKG

@rcsb/rcsb-saguaro

Version:
190 lines (189 loc) 9.51 kB
import { __awaiter } from "tslib"; import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime"; import React from "react"; import * as classes from "../../scss/RcsbFvRow.module.scss"; import { CSSTransition } from "react-transition-group"; import { RcsbFvDefaultConfigValues } from "../RcsbFvConfig/RcsbFvDefaultConfigValues"; import { EventType } from "../RcsbFvContextManager/RcsbFvContextManager"; import { asyncScheduler } from "rxjs"; import { computePosition, detectOverflow } from "@floating-ui/dom"; import BxPlus from "./icons/bx-plus.svg"; import BxMinus from "./icons/bx-minus.svg"; import BxRight from "./icons/bx-right-arrow.svg"; import BxLeft from "./icons/bx-left-arrow.svg"; import BxDown from "./icons/bxs-down-arrow.svg"; export class RcsbFvUI extends React.Component { constructor() { super(...arguments); this.collapseRef = React.createRef(); this.expandRef = React.createRef(); /**UI config Object*/ this.config = [{ icon: _jsx(BxPlus, Object.assign({}, RcsbFvUI.ICON_PROPS)), callback: this.zoomIn.bind(this), name: "zoom-in" }, { icon: _jsx(BxMinus, Object.assign({}, RcsbFvUI.ICON_PROPS)), callback: this.zoomOut.bind(this), name: "zoom-out" }, { icon: _jsx(BxRight, Object.assign({}, RcsbFvUI.ICON_PROPS)), callback: this.move.bind(this, 1), name: "move-right" }, { icon: _jsx(BxLeft, Object.assign({}, RcsbFvUI.ICON_PROPS)), callback: this.move.bind(this, -1), name: "move-left" }]; this.hideTask = null; this.state = { collapse: false }; } render() { return (_jsx("div", { id: this.props.boardId + "_uiDiv" /* RcsbFvDOMConstants.UI_DOM_ID_PREFIX */, className: classes.rcsbUI + " " + classes.rcsbSmoothDivHide, style: { position: "absolute", top: 0, left: 0 }, children: _jsxs("div", { style: { position: "relative" }, children: [_jsx(CSSTransition, { in: this.state.collapse, timeout: 300, classNames: classes.rcsbCollapseUI, nodeRef: this.collapseRef, children: _jsx("div", { style: { position: "absolute" }, className: classes.rcsbCollapsedUIDiv + " " + classes.rcsbCollapseUI, onMouseEnter: this.changeState.bind(this, { collapse: false }), ref: this.collapseRef, children: _jsx("div", { className: classes.rcsbCollapsedIcon, children: _jsx(BxDown, Object.assign({}, RcsbFvUI.ICON_PROPS)) }) }) }), _jsx(CSSTransition, { in: !this.state.collapse, timeout: 300, classNames: classes.rcsbExpandUI, nodeRef: this.expandRef, children: _jsx("div", { style: { position: "absolute" }, className: classes.rcsbExpandUI, onMouseLeave: this.changeState.bind(this, { collapse: true }), ref: this.expandRef, children: this.config.map(button => { return this.buildButton(button); }) }) })] }) })); } componentDidMount() { this.subscription = this.subscribe(); const refDiv = document.querySelector("#" + this.props.boardId); if (refDiv == null) throw "Main board DOM element not found"; this.refDiv = refDiv; const tooltipDiv = document.querySelector("#" + this.props.boardId + "_uiDiv" /* RcsbFvDOMConstants.UI_DOM_ID_PREFIX */); if (tooltipDiv == null) throw "Tooltip DOM element not found"; this.tooltipDiv = tooltipDiv; } componentWillUnmount() { this.subscription.unsubscribe(); } subscribe() { return this.props.contextManager.subscribe((o) => { switch (o.eventType) { case EventType.BOARD_HOVER: this.boardHover(o.eventData); break; } }); } boardHover(flag) { if (flag) { this.displayUI(); } else { this.hideUI(); } } displayUI() { if (this.hideTask) this.hideTask.unsubscribe(); const offsetHeight = this.props.boardConfigData.includeAxis === true ? RcsbFvDefaultConfigValues.trackAxisHeight + 2 : 0; computePosition(this.refDiv, this.tooltipDiv, { placement: 'right-start', middleware: [{ name: 'middleware', fn(middlewareArguments) { return __awaiter(this, void 0, void 0, function* () { const overflow = yield detectOverflow(middlewareArguments, { rootBoundary: "viewport" }); if (overflow.top > offsetHeight) return { y: overflow.top + middlewareArguments.y - offsetHeight }; return {}; }); }, }] }).then(({ x, y }) => { Object.assign(this.tooltipDiv.style, { left: `${x}px`, top: `${y + offsetHeight}px` }); }); this.tooltipDiv.classList.remove(classes.rcsbSmoothDivHide); this.tooltipDiv.classList.add(classes.rcsbSmoothDivDisplay); } hideUI() { const tooltipDiv = document.querySelector("#" + this.props.boardId + "_uiDiv" /* RcsbFvDOMConstants.UI_DOM_ID_PREFIX */); if (tooltipDiv == null) return; this.hideTask = asyncScheduler.schedule(() => { tooltipDiv.classList.remove(classes.rcsbSmoothDivDisplay); tooltipDiv.classList.add(classes.rcsbSmoothDivHide); }, 300); } buildButton(buttonConfig) { return (_jsx("div", { className: classes.rcsbUIButton, children: _jsx("div", { className: classes.rcsbIcon, onClick: buttonConfig.callback, children: buttonConfig.icon }) }, buttonConfig.name)); } changeState(state) { this.setState(state); } /*************** ** UI methods ** ****************/ zoomIn() { const max = this.props.boardConfigData.range != null ? this.props.boardConfigData.range.max : this.props.boardConfigData.length; if (max == null) return; const currentDomain = this.props.xScale.domain(); const deltaZoom = Math.floor((currentDomain[1] - currentDomain[0]) * 0.1); const x = currentDomain[0] + deltaZoom; const y = currentDomain[1] - deltaZoom; if ((y - x) > 20) this.setDomain({ domain: [x, y] }); } zoomOut() { const max = this.props.boardConfigData.range != null ? this.props.boardConfigData.range.max : this.props.boardConfigData.length; const min = this.props.boardConfigData.range != null ? this.props.boardConfigData.range.min : 1; if (max == null) return; const currentDomain = this.props.xScale.domain(); const deltaZoom = Math.floor((currentDomain[1] - currentDomain[0]) * 0.1); const x = currentDomain[0] - deltaZoom > (min - RcsbFvDefaultConfigValues.increasedView) ? currentDomain[0] - deltaZoom : (min - RcsbFvDefaultConfigValues.increasedView); const y = currentDomain[1] + deltaZoom < max + RcsbFvDefaultConfigValues.increasedView ? currentDomain[1] + deltaZoom : max + RcsbFvDefaultConfigValues.increasedView; if ((y - x) < (max + RcsbFvDefaultConfigValues.increasedView)) this.setDomain({ domain: [x, y] }); else this.setDomain({ domain: [(min - RcsbFvDefaultConfigValues.increasedView), max + RcsbFvDefaultConfigValues.increasedView] }); } move(direction) { const max = this.props.boardConfigData.range != null ? this.props.boardConfigData.range.max : this.props.boardConfigData.length; const min = this.props.boardConfigData.range != null ? this.props.boardConfigData.range.min : 1; if (max == null) return; const currentDomain = this.props.xScale.domain(); let deltaZoom = Math.floor((currentDomain[1] - currentDomain[0]) * 0.1); if (currentDomain[0] + direction * deltaZoom < (min - RcsbFvDefaultConfigValues.increasedView)) deltaZoom = currentDomain[0] - (min - RcsbFvDefaultConfigValues.increasedView); else if (currentDomain[1] + direction * deltaZoom > (max + RcsbFvDefaultConfigValues.increasedView)) deltaZoom = max + RcsbFvDefaultConfigValues.increasedView - currentDomain[1]; const x = currentDomain[0] + direction * deltaZoom; const y = currentDomain[1] + direction * deltaZoom; if ((y - x) < (max + RcsbFvDefaultConfigValues.increasedView)) this.setDomain({ domain: [x, y] }); else this.setDomain({ domain: [(min - RcsbFvDefaultConfigValues.increasedView), max + RcsbFvDefaultConfigValues.increasedView] }); } /**Force all board track annotation cells to set xScale. Called when a new track has been added*/ setScale() { if (this.props.xScale != null) { this.props.contextManager.next({ eventType: EventType.SCALE, eventData: this.props.boardId }); } } /**Update d3 xScale domain * @param domainData new xScale domain * */ setDomain(domainData) { this.props.xScale.domain(domainData.domain); this.setScale(); } } RcsbFvUI.ICON_PROPS = { width: 16, height: 16, viewBox: "0 0 24 24" };