UNPKG

@rcsb/rcsb-saguaro

Version:
318 lines (317 loc) 11.9 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.RcsbFv = void 0; const tslib_1 = require("tslib"); const jsx_runtime_1 = require("react/jsx-runtime"); const client_1 = require("react-dom/client"); const RcsbFvBoard_1 = require("./RcsbFvBoard/RcsbFvBoard"); const RcsbFvContextManager_1 = require("./RcsbFvContextManager/RcsbFvContextManager"); const RcsbSelection_1 = require("../RcsbBoard/RcsbSelection"); const RcsbD3ScaleFactory_1 = require("../RcsbBoard/RcsbD3/RcsbD3ScaleFactory"); const BoardDataState_1 = require("./RcsbFvBoard/Utils/BoardDataState"); const uniqid_1 = tslib_1.__importDefault(require("uniqid")); const RcsbFvStateManager_1 = require("./RcsbFvState/RcsbFvStateManager"); const RcsbFvDefaultConfigValues_1 = require("./RcsbFvConfig/RcsbFvDefaultConfigValues"); const rxjs_1 = require("rxjs"); /** * Protein Feature Viewer entry point */ class RcsbFv { constructor(props) { /**rxjs event based handler used to communicate events (click, highlight, move) between board tracks*/ this.contextManager = new RcsbFvContextManager_1.RcsbFvContextManager(); /**Flag indicating that the React component has been mounted*/ this.mounted = false; /**Global d3 Xscale object shared among all board tracks*/ this.xScale = RcsbD3ScaleFactory_1.RcsbD3ScaleFactory.getLinearScale(); /**Global selection shared among all tracks*/ this.selection = new RcsbSelection_1.RcsbSelection(); this.boardId = (0, uniqid_1.default)("RcsbFvBoard_"); this.boardConfigData = props.boardConfigData; this.elementId = props.elementId; const node = document.getElementById(this.elementId); if (!node) throw new Error(`HTML element ${this.elementId} not found`); this.node = node; this.boardDataSate = new BoardDataState_1.BoardDataState({ contextManager: this.contextManager, boardId: this.boardId, rowConfigData: props.rowConfigData }); this.rcsbFvStateManager = new RcsbFvStateManager_1.RcsbFvStateManager({ xScale: this.xScale, selection: this.selection, contextManager: this.contextManager, boardId: this.boardId }); if (this.boardConfigData != null) { this.init().then(() => { console.info(`PFV ${this.elementId} is ready. Configuration provided during object instantiation`); }); } } /** * Loads the configuration for each row of the board * @param rowConfigData Array of configurations for each row in the board */ setBoardData(rowConfigData) { this.boardDataSate.setBoardData(rowConfigData); return this.updateBoardData(); } /** * Gets the configuration for each row of the board */ getBoardData() { return this.boardDataSate.getBoardData(); } /** * Loads the configuration of the board * @param config Configuration of the board */ setBoardConfig(config) { return this.updateBoardConfig({ boardConfigData: config }); } /** * @return board configuration */ getBoardConfig() { return this.boardConfigData; } then(f) { if (this.rcsbFvPromise) this.rcsbFvPromise.then(() => { f(); }); else throw "RcsbFv init method was not called"; return this; } catch(f) { if (this.rcsbFvPromise) this.rcsbFvPromise.catch(() => { f(); }); else throw "RcsbFv init method was not called"; return this; } /**Renders the board*/ init() { this.rcsbFvPromise = new Promise((resolve, reject) => { if (!this.mounted && this.boardConfigData != undefined) { this.reactRoot = (0, client_1.createRoot)(this.node); this.reactRoot.render((0, jsx_runtime_1.jsx)(RcsbFvBoard_1.RcsbFvBoard, { boardId: this.boardId, rowConfigData: this.boardDataSate.getBoardData(), boardConfigData: this.boardConfigWithTrackWidth(), contextManager: this.contextManager, xScale: this.xScale, selection: this.selection, resolve: resolve })); this.mounted = true; } else { reject("FATAL ERROR: RcsvFvBoard is mounted or board configuration was not loaded"); } }); return this.rcsbFvPromise.then(() => this.addResizeObserver()); } /**Unmount the board*/ unmount() { var _a; if (this.reactRoot != null) { this.reactRoot.unmount(); } this.boardDataSate.unsubscribe(); this.rcsbFvStateManager.unsubscribe(); (_a = this.resizeObserver) === null || _a === void 0 ? void 0 : _a.unobserve(this.node); } /**Returns all track Ids in the same order that are visualised in the board*/ getTrackIds() { return this.boardDataSate.getBoardData().map(r => r.trackId); } /**Adds new annotations for a particular board track * @param trackId Id that identifies the track * @param trackData Annotations to be added in the track * */ addTrackData(trackId, trackData) { this.boardDataSate.addTrackData({ trackId, trackData }); return this.updateBoardData(); } /**Replaces annotations a particular board track * @param trackId Id that identifies the track * @param trackData New annotations to be displayed * @param displayId optional Id that identifies track from a composite track to update * */ updateTrackData(trackId, trackData, displayId) { this.boardDataSate.updateTrackData({ trackId, trackData, displayId }); return this.updateBoardData(); } /**Method used to update board global and all-tracks configuration * @param newConfig New board configuration data * */ updateBoardConfig(newConfig) { if (newConfig.rowConfigData) this.boardDataSate.setBoardData(newConfig.rowConfigData); else this.boardDataSate.refresh(); if (newConfig.boardConfigData) this.boardConfigData = Object.assign(Object.assign({}, this.boardConfigData), newConfig.boardConfigData); const configDataObj = { rowConfigData: this.boardDataSate.getBoardData(), boardConfigData: newConfig.boardConfigData ? this.boardConfigData : undefined }; return new Promise((resolve, reject) => { this.contextManager.next({ eventType: RcsbFvContextManager_1.EventType.UPDATE_BOARD_CONFIG, eventData: configDataObj, eventResolve: resolve }); }); } /**Rerender the board track * @param trackId Id that identifies the track * */ resetTrack(trackId) { this.boardDataSate.resetTrack(trackId); return this.updateBoardData(); } /**Adds a new track to the board * @param trackConfig Track configuration data * */ addTrack(trackConfig) { this.boardDataSate.addTrack(trackConfig); return this.updateBoardData(); } /**Changes track visibility (true/false) * @param obj Track visibility event data **/ changeTrackVisibility(obj) { this.boardDataSate.changeTrackVisibility(obj); return this.updateBoardData(); } /**Change board view range * @param domain new xScale domain **/ setDomain(domain) { this.contextManager.next({ eventType: RcsbFvContextManager_1.EventType.DOMAIN_VIEW, eventData: { domain: domain } }); } /**Get board view range **/ getDomain() { return [this.xScale.domain()[0], this.xScale.domain()[1]]; } /**Select board range * @param selection region/elements **/ setSelection(selection) { this.contextManager.next({ eventType: RcsbFvContextManager_1.EventType.SET_SELECTION, eventData: selection }); } /**Get selected board ranges * @param mode selection type **/ getSelection(mode) { return this.selection.getSelected(mode); } /**Add selection to board * @param selection region/elements **/ addSelection(selection) { this.contextManager.next({ eventType: RcsbFvContextManager_1.EventType.ADD_SELECTION, eventData: selection }); } /** * Clear Selection **/ clearSelection(mode) { this.contextManager.next({ eventType: RcsbFvContextManager_1.EventType.SET_SELECTION, eventData: { elements: null, mode: mode !== null && mode !== void 0 ? mode : 'select' } }); } /** * Move board track * @param oldIndex original position * @param newIndex new position * **/ moveTrack(oldIndex, newIndex) { this.boardDataSate.moveTrack({ oldIndex, newIndex }); return this.updateBoardData(); } /** * reset Selection and Scale **/ reset() { this.selection.reset(); this.xScale.reset(); } updateBoardData() { return new Promise((resolve, reject) => { this.contextManager.next({ eventType: RcsbFvContextManager_1.EventType.UPDATE_BOARD_CONFIG, eventData: { rowConfigData: this.boardDataSate.getBoardData() }, eventResolve: resolve }); }); } boardConfigWithTrackWidth() { var _a; return Object.assign(Object.assign({}, this.boardConfigData), { trackWidth: (_a = this.boardConfigData.trackWidth) !== null && _a !== void 0 ? _a : (this.node.getBoundingClientRect().width - this.rowTitleWidth()) }); } addResizeObserver() { if (this.boardConfigData.trackWidth) return; this.resizeObserver = resizeBoard(this.node, (width) => tslib_1.__awaiter(this, void 0, void 0, function* () { const trackWidth = width - this.rowTitleWidth(); if (trackWidth <= 0) { console.debug(`Element width ${width} is too small. Row title width ${this.rowTitleWidth()}. Not rendering`); return; } const selected = this.getSelection("select").map(s => ({ begin: s.rcsbFvTrackDataElement.begin, end: s.rcsbFvTrackDataElement.end })); const domain = [this.xScale.domain()[0], this.xScale.domain()[1]]; const data = this.boardDataSate.getBoardData(); yield this.updateBoardConfig({ boardConfigData: { trackWidth: trackWidth }, rowConfigData: [] }); this.setDomain(domain); this.setSelection({ mode: "select", elements: selected }); yield this.updateBoardConfig({ rowConfigData: data }); })); } rowTitleWidth() { var _a; return ((_a = this.boardConfigData.rowTitleWidth) !== null && _a !== void 0 ? _a : RcsbFvDefaultConfigValues_1.RcsbFvDefaultConfigValues.rowTitleWidth) + 40; } } exports.RcsbFv = RcsbFv; function resizeBoard(node, callback) { let width = node.getBoundingClientRect().width; let task; const resizeObserver = new ResizeObserver((entries, observer) => { if (width == entries[0].contentRect.width) return; width = entries[0].contentRect.width; if (task) task.unsubscribe(); task = rxjs_1.asyncScheduler.schedule(() => callback(width), 50); }); resizeObserver.observe(node); return resizeObserver; }