"use strict"; var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault"); Object.defineProperty(exports, "__esModule", { value: true }); exports.Embeddable = void 0; var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty")); var _fastDeepEqual = _interopRequireDefault(require("fast-deep-equal")); var _lodash = require("lodash"); var Rx = _interopRequireWildcard(require("rxjs")); var _operators = require("rxjs/operators"); var _public = require("@kbn/kibana-utils-plugin/public"); var _types = require("../../../common/types"); var _diff_embeddable_input = require("./diff_embeddable_input"); function _getRequireWildcardCache(nodeInterop) { if (typeof WeakMap !== "function") return null; var cacheBabelInterop = new WeakMap(); var cacheNodeInterop = new WeakMap(); return (_getRequireWildcardCache = function (nodeInterop) { return nodeInterop ? cacheNodeInterop : cacheBabelInterop; })(nodeInterop); } function _interopRequireWildcard(obj, nodeInterop) { if (!nodeInterop && obj && obj.__esModule) { return obj; } if (obj === null || typeof obj !== "object" && typeof obj !== "function") { return { default: obj }; } var cache = _getRequireWildcardCache(nodeInterop); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj.default = obj; if (cache) { cache.set(obj, newObj); } return newObj; } /* * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one * or more contributor license agreements. Licensed under the Elastic License * 2.0 and the Server Side Public License, v 1; you may not use this file except * in compliance with, at your election, the Elastic License 2.0 or the Server * Side Public License, v 1. */ function getPanelTitle(input, output) { var _input$title; if (input.hidePanelTitles) return ''; return (_input$title = input.title) !== null && _input$title !== void 0 ? _input$title : output.defaultTitle; } function getPanelDescription(input, output) { var _input$description; if (input.hidePanelTitles) return ''; return (_input$description = input.description) !== null && _input$description !== void 0 ? _input$description : output.defaultDescription; } class Embeddable { constructor(input, output, parent) { (0, _defineProperty2.default)(this, "runtimeId", Embeddable.runtimeId++); (0, _defineProperty2.default)(this, "parent", void 0); (0, _defineProperty2.default)(this, "isContainer", false); (0, _defineProperty2.default)(this, "deferEmbeddableLoad", false); (0, _defineProperty2.default)(this, "id", void 0); (0, _defineProperty2.default)(this, "fatalError", void 0); (0, _defineProperty2.default)(this, "output", void 0); (0, _defineProperty2.default)(this, "input", void 0); (0, _defineProperty2.default)(this, "inputSubject", new Rx.ReplaySubject(1)); (0, _defineProperty2.default)(this, "outputSubject", new Rx.ReplaySubject(1)); (0, _defineProperty2.default)(this, "input$", this.inputSubject.asObservable()); (0, _defineProperty2.default)(this, "output$", this.outputSubject.asObservable()); (0, _defineProperty2.default)(this, "renderComplete", new _public.RenderCompleteDispatcher()); // Listener to parent changes, if this embeddable exists in a parent, in order // to update input when the parent changes. (0, _defineProperty2.default)(this, "parentSubscription", void 0); (0, _defineProperty2.default)(this, "destroyed", false); this.id = input.id; this.output = { title: getPanelTitle(input, output), description: getPanelDescription(input, output), ...(this.reportsEmbeddableLoad() ? {} : { loading: false, rendered: true }), ...output }; this.input = { viewMode: _types.ViewMode.EDIT, ...input }; this.parent = parent; this.inputSubject.next(this.input); this.outputSubject.next(this.output); if (parent) { this.parentSubscription = Rx.merge(parent.getInput$(), parent.getOutput$()).subscribe(() => { // Make sure this panel hasn't been removed immediately after it was added, but before it finished loading. if (!parent.getInput().panels[this.id]) return; const newInput = parent.getInputForChild(this.id); this.onResetInput(newInput); }); } this.getOutput$().pipe((0, _operators.map)(({ title }) => title || ''), (0, _operators.distinctUntilChanged)()).subscribe(title => this.renderComplete.setTitle(title)); } reportsEmbeddableLoad() { return false; } refreshInputFromParent() { if (!this.parent) return; // Make sure this panel hasn't been removed immediately after it was added, but before it finished loading. if (!this.parent.getInput().panels[this.id]) return; const newInput = this.parent.getInputForChild(this.id); this.onResetInput(newInput); } getIsContainer() { return this.isContainer === true; } /** * Reload will be called when there is a request to refresh the data or view, even if the * input data did not change. * * In case if input data did change and reload is requested input$ and output$ would still emit before `reload` is called * * The order would be as follows: * input$ * output$ * reload() * ---- * updated$ */ /** * Merges input$ and output$ streams and debounces emit till next macro-task. * Could be useful to batch reactions to input$ and output$ updates that happen separately but synchronously. * In case corresponding state change triggered `reload` this stream is guarantied to emit later, * which allows to skip state handling in case `reload` already handled it. */ getUpdated$() { return (0, Rx.merge)(this.getInput$().pipe((0, _operators.skip)(1)), this.getOutput$().pipe((0, _operators.skip)(1))).pipe((0, _operators.debounceTime)(0)); } getInput$() { return this.input$; } getOutput$() { return this.output$; } getOutput() { return this.output; } async getExplicitInputIsEqual(lastExplicitInput) { const currentExplicitInput = this.getExplicitInput(); return (0, _diff_embeddable_input.genericEmbeddableInputIsEqual)(lastExplicitInput, currentExplicitInput) && (0, _fastDeepEqual.default)((0, _diff_embeddable_input.omitGenericEmbeddableInput)(lastExplicitInput), (0, _diff_embeddable_input.omitGenericEmbeddableInput)(currentExplicitInput)); } getExplicitInput() { const root = this.getRoot(); if (root.getIsContainer()) { var _ref, _root$getInput$panels, _root$getInput$panels2; return (_ref = (_root$getInput$panels = root.getInput().panels) === null || _root$getInput$panels === void 0 ? void 0 : (_root$getInput$panels2 = _root$getInput$panels[this.id]) === null || _root$getInput$panels2 === void 0 ? void 0 : _root$getInput$panels2.explicitInput) !== null && _ref !== void 0 ? _ref : this.getInput(); } return this.getInput(); } getPersistableInput() { return this.getExplicitInput(); } getInput() { return this.input; } getTitle() { var _this$output$title; return (_this$output$title = this.output.title) !== null && _this$output$title !== void 0 ? _this$output$title : ''; } getDescription() { var _this$output$descript; return (_this$output$descript = this.output.description) !== null && _this$output$descript !== void 0 ? _this$output$descript : ''; } /** * Returns the top most parent embeddable, or itself if this embeddable * is not within a parent. */ getRoot() { let root = this; while (root.parent) { root = root.parent; } return root; } updateInput(changes) { if (this.destroyed) { throw new Error('Embeddable has been destroyed'); } if (this.parent) { // Ensures state changes flow from container downward. this.parent.updateInputForChild(this.id, changes); } else { this.onInputChanged(changes); } } render(el) { this.renderComplete.setEl(el); this.renderComplete.setTitle(this.output.title || ''); if (this.destroyed) { throw new Error('Embeddable has been destroyed'); } } /** * An embeddable can return inspector adapters if it want the inspector to be * available via the context menu of that panel. * @return Inspector adapters that will be used to open an inspector for. */ getInspectorAdapters() { return undefined; } /** * Called when this embeddable is no longer used, this should be the place for * implementors to add additional clean up tasks, like un-mounting and unsubscribing. */ destroy() { this.destroyed = true; this.inputSubject.complete(); this.outputSubject.complete(); if (this.parentSubscription) { this.parentSubscription.unsubscribe(); } return; } /** * communicate to the parent embeddable that this embeddable's initialization is finished. * This only applies to embeddables which defer their loading state with deferEmbeddableLoad. */ setInitializationFinished() { var _this$parent; if (this.deferEmbeddableLoad && (_this$parent = this.parent) !== null && _this$parent !== void 0 && _this$parent.isContainer) { this.parent.setChildLoaded(this); } } updateOutput(outputChanges) { const newOutput = { ...this.output, ...outputChanges }; if (!(0, _fastDeepEqual.default)(this.output, newOutput)) { this.output = newOutput; this.outputSubject.next(this.output); } } /** * Call this **only** when your embeddable has encountered a non-recoverable error; recoverable errors * should be handled by the individual embeddable types * @param e The fatal, unrecoverable Error that was thrown */ onFatalError(e) { var _this$parent2; this.fatalError = e; this.outputSubject.error(e); // if the container is waiting for this embeddable to complete loading, // a fatal error counts as complete. if (this.deferEmbeddableLoad && (_this$parent2 = this.parent) !== null && _this$parent2 !== void 0 && _this$parent2.isContainer) { this.parent.setChildLoaded(this); } } onResetInput(newInput) { if (!(0, _fastDeepEqual.default)(this.input, newInput)) { const oldLastReloadRequestTime = this.input.lastReloadRequestTime; this.input = newInput; this.inputSubject.next(newInput); this.updateOutput({ title: getPanelTitle(this.input, this.output), description: getPanelDescription(this.input, this.output) }); if (oldLastReloadRequestTime !== newInput.lastReloadRequestTime) { this.reload(); } } } onInputChanged(changes) { const newInput = (0, _lodash.cloneDeep)({ ...this.input, ...changes }); this.onResetInput(newInput); } supportedTriggers() { return []; } } exports.Embeddable = Embeddable; (0, _defineProperty2.default)(Embeddable, "runtimeId", 0);