UNPKG

@deephaven/golden-layout

Version:

A multi-screen javascript Layout manager

150 lines (142 loc) 6.36 kB
function _defineProperty(e, r, t) { return (r = _toPropertyKey(r)) in e ? Object.defineProperty(e, r, { value: t, enumerable: !0, configurable: !0, writable: !0 }) : e[r] = t, e; } function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == typeof i ? i : i + ""; } function _toPrimitive(t, r) { if ("object" != typeof t || !t) return t; var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r || "default"); if ("object" != typeof i) return i; throw new TypeError("@@toPrimitive must return a primitive value."); } return ("string" === r ? String : Number)(t); } import React, { Component } from 'react'; import ReactDOM from 'react-dom'; import $ from 'jquery'; /** * A specialised GoldenLayout component that binds GoldenLayout container * lifecycle events to react components * * @param container * @param state state is not required for react components */ export default class ReactComponentHandler { constructor(container, state) { _defineProperty(this, "_container", void 0); _defineProperty(this, "_reactComponent", null); _defineProperty(this, "_portalComponent", null); _defineProperty(this, "_originalComponentWillUpdate", undefined); _defineProperty(this, "_initialState", void 0); _defineProperty(this, "_reactClass", void 0); this._reactComponent = null; this._portalComponent = null; this._originalComponentWillUpdate = undefined; this._container = container; this._initialState = state; this._reactClass = this._getReactClass(); this._container.on('open', this._render, this); this._container.on('destroy', this._destroy, this); } /** * Gets the unique key to use for the react component * @returns Unique key for the component */ _key() { var id = this._container._config.id; if (!id) { throw new Error('Cannot mount panel without id'); } // If addId is called multiple times, an element can have multiple IDs in golden-layout // We don't use it, but changing the type requires many changes and a separate PR if (Array.isArray(id)) { return id.join(','); } return id; } /** * Creates the react class and component and hydrates it with * the initial state - if one is present * * By default, react's getInitialState will be used * * Creates a portal so the non-react golden-layout code still works, * but also allows us to mount the React components in the app's tree * instead of separate sibling root trees */ _render() { var key = this._key(); this._portalComponent = /*#__PURE__*/ReactDOM.createPortal(this._getReactComponent(), this._container.getElement()[0], key); this._container.layoutManager.addReactChild(key, this._portalComponent); } /** * Fired by react when the component is created. * <p> * Note: This callback is used instead of the return from `ReactDOM.render` because * of https://github.com/facebook/react/issues/10309. * </p> * * @param component The component instance created by the `ReactDOM.render` call in the `_render` method. */ _gotReactComponent(component) { if (!component) { return; } this._reactComponent = component; // Class components manipulate the lifecycle to hook into state changes // Functional components can save data with the usePersistentState hook if (this._reactComponent instanceof Component) { this._originalComponentWillUpdate = this._reactComponent.componentWillUpdate; this._reactComponent.componentWillUpdate = this._onUpdate.bind(this); var state = this._container.getState(); if (state) { this._reactComponent.setState(state); } } } /** * Removes the component from the DOM and thus invokes React's unmount lifecycle */ _destroy() { this._container.layoutManager.removeReactChild(this._key(), this._portalComponent); this._container.off('open', this._render, this); this._container.off('destroy', this._destroy, this); } /** * Hooks into React's state management and applies the componentstate * to GoldenLayout */ _onUpdate(nextProps, nextState) { var _this$_originalCompon; this._container.setState(nextState); (_this$_originalCompon = this._originalComponentWillUpdate) === null || _this$_originalCompon === void 0 || _this$_originalCompon.call(this._reactComponent, nextProps, nextState, undefined); } /** * Retrieves the react class from GoldenLayout's registry * @returns react class */ _getReactClass() { var componentName = this._container._config.component; if (!componentName) { throw new Error('No react component name. type: react-component needs a field `component`'); } var reactClass = this._container.layoutManager.getComponent(componentName) || this._container.layoutManager.getFallbackComponent(); if (!reactClass) { throw new Error('React component "' + componentName + '" not found. ' + 'Please register all components with GoldenLayout using `registerComponent(name, component)`'); } return reactClass; } /** * Copies and extends the properties array and returns the React element */ _getReactComponent() { var defaultProps = { glEventHub: this._container.layoutManager.eventHub, glContainer: this._container, /** * This ref assignment makes use of callback refs which is a legacy ref style in React. * It uses the callback to inject GoldenLayout _onUpdate into the React componentWillUpdate lifecycle. * This allows GoldenLayout to track the internal state of class components. * We then emit this state change and somewhere furter up, serialize it. * Specifically we look for state.panelState changes. * We should not do this going forward as it's quite unclear where/why your component's state might be saved. * This ref cannot be removed unless we refactor all existing panels to not rely on the magic of panelState. * DashboardUtils#dehydrate is where the panelState gets read/saved. */ ref: this._gotReactComponent.bind(this) }; var props = $.extend(defaultProps, this._container._config.props); return /*#__PURE__*/React.createElement(this._reactClass, props); } } //# sourceMappingURL=ReactComponentHandler.js.map