"use strict";

var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.default = void 0;
Object.defineProperty(exports, "getInlineNodeViewProducer", {
  enumerable: true,
  get: function get() {
    return _getInlineNodeViewProducer.getInlineNodeViewProducer;
  }
});
Object.defineProperty(exports, "inlineNodeViewClassname", {
  enumerable: true,
  get: function get() {
    return _getInlineNodeViewProducer.inlineNodeViewClassname;
  }
});
var _extends2 = _interopRequireDefault(require("@babel/runtime/helpers/extends"));
var _classCallCheck2 = _interopRequireDefault(require("@babel/runtime/helpers/classCallCheck"));
var _createClass2 = _interopRequireDefault(require("@babel/runtime/helpers/createClass"));
var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty"));
var _react = _interopRequireDefault(require("react"));
var _analytics = require("../analytics");
var _eventDispatcher = require("../event-dispatcher");
var _ErrorBoundary = require("../ui/ErrorBoundary");
var _utils = require("../utils");
var _analytics2 = require("../utils/analytics");
var _generateUniqueNodeKey = require("./generateUniqueNodeKey");
var _getInlineNodeViewProducer = require("./getInlineNodeViewProducer");
// Disable no-re-export rule for entry point files
/* eslint-disable @atlaskit/editor/no-re-export */
var ReactNodeView = exports.default = /*#__PURE__*/function () {
  function ReactNodeView(_node, view, getPos, portalProviderAPI, eventDispatcher, reactComponentProps,
  // Spreading props to pass through dynamic component props
  // Ignored via go/ees005
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
  reactComponent, viewShouldUpdate, shouldRenderImmediatelyInPortal) {
    var _this = this;
    (0, _classCallCheck2.default)(this, ReactNodeView);
    // Spreading props to pass through dynamic component props
    // Ignored via go/ees005
    // eslint-disable-next-line @typescript-eslint/no-explicit-any
    (0, _defineProperty2.default)(this, "decorations", []);
    (0, _defineProperty2.default)(this, "handleRef", function (node) {
      return _this._handleRef(node);
    });
    (0, _defineProperty2.default)(this, "dispatchAnalyticsEvent", function (payload) {
      if (_this.eventDispatcher) {
        var dispatch = (0, _eventDispatcher.createDispatch)(_this.eventDispatcher);
        dispatch(_analytics2.analyticsEventKey, {
          payload: payload
        });
      }
    });
    this.node = _node;
    this.view = view;
    this.getPos = getPos;
    this.portalProviderAPI = portalProviderAPI;
    this.reactComponentProps = reactComponentProps || {};
    this.reactComponent = reactComponent;
    this._viewShouldUpdate = viewShouldUpdate;
    this.eventDispatcher = eventDispatcher;
    this.key = (0, _generateUniqueNodeKey.generateUniqueNodeKey)();
    this.shouldRenderImmediatelyInPortal = shouldRenderImmediatelyInPortal || false;
  }

  /**
   * This method exists to move initialization logic out of the constructor,
   * so object can be initialized properly before calling render first time.
   *
   * Example:
   * Instance properties get added to an object only after super call in
   * constructor, which leads to some methods being undefined during the
   * first render.
   */
  return (0, _createClass2.default)(ReactNodeView, [{
    key: "init",
    value: function init() {
      var _this2 = this;
      this.domRef = this.createDomRef();
      this.setDomAttrs(this.node, this.domRef);
      var _ref = this.getContentDOM() || {
          dom: undefined,
          contentDOM: undefined
        },
        contentDOMWrapper = _ref.dom,
        contentDOM = _ref.contentDOM;
      if (this.domRef && contentDOMWrapper) {
        this.domRef.appendChild(contentDOMWrapper);
        this.contentDOM = contentDOM ? contentDOM : contentDOMWrapper;
        this.contentDOMWrapper = contentDOMWrapper || contentDOM;
      }

      // @see ED-3790
      // something gets messed up during mutation processing inside of a
      // nodeView if DOM structure has nested plain "div"s, it doesn't see the
      // difference between them and it kills the nodeView
      this.domRef.classList.add("".concat(this.node.type.name, "View-content-wrap"));
      var _getPerformanceOption = (0, _utils.getPerformanceOptions)(this.view),
        samplingRate = _getPerformanceOption.samplingRate,
        slowThreshold = _getPerformanceOption.slowThreshold,
        trackingEnabled = _getPerformanceOption.trackingEnabled;
      trackingEnabled && (0, _utils.startMeasureReactNodeViewRendered)({
        nodeTypeName: this.node.type.name
      });
      this.renderReactComponent(function () {
        return _this2.render(_this2.reactComponentProps, _this2.handleRef);
      });
      trackingEnabled && (0, _utils.stopMeasureReactNodeViewRendered)({
        nodeTypeName: this.node.type.name,
        dispatchAnalyticsEvent: this.dispatchAnalyticsEvent,
        samplingRate: samplingRate,
        slowThreshold: slowThreshold
      });
      return this;
    }

    // Spreading props to pass through dynamic component props
    // Ignored via go/ees005
    // eslint-disable-next-line @typescript-eslint/no-explicit-any
  }, {
    key: "renderReactComponent",
    value: function renderReactComponent(component) {
      var _this3 = this;
      if (!this.domRef || !component) {
        return;
      }
      var componentWithErrorBoundary = function componentWithErrorBoundary() {
        var _this3$node$type$name, _this3$node;
        return /*#__PURE__*/_react.default.createElement(_ErrorBoundary.ErrorBoundary, {
          component: _analytics.ACTION_SUBJECT.REACT_NODE_VIEW,
          componentId: (_this3$node$type$name = _this3 === null || _this3 === void 0 || (_this3$node = _this3.node) === null || _this3$node === void 0 || (_this3$node = _this3$node.type) === null || _this3$node === void 0 ? void 0 : _this3$node.name) !== null && _this3$node$type$name !== void 0 ? _this3$node$type$name : _analytics.ACTION_SUBJECT_ID.UNKNOWN_NODE,
          dispatchAnalyticsEvent: _this3.dispatchAnalyticsEvent
        }, component());
      };
      this.portalProviderAPI.render(componentWithErrorBoundary,
      // Spreading props to pass through dynamic component props
      // Ignored via go/ees005
      // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
      this.domRef, this.key, undefined, this.shouldRenderImmediatelyInPortal);
    }
  }, {
    key: "createDomRef",
    value: function createDomRef() {
      if (!this.node.isInline) {
        return document.createElement('div');
      }
      var htmlElement = document.createElement('span');
      return htmlElement;
    }
  }, {
    key: "getContentDOM",
    value: function getContentDOM() {
      return undefined;
    }
  }, {
    key: "_handleRef",
    value: function _handleRef(node) {
      var _this4 = this;
      var contentDOM = this.contentDOMWrapper || this.contentDOM;
      // @ts-ignore
      // Spreading props to pass through dynamic component props
      // Ignored via go/ees005
      // eslint-disable-next-line @typescript-eslint/no-explicit-any
      var oldIgnoreMutation;
      var selectionBookmark;
      var mutationsIgnored = false;

      // move the contentDOM node inside the inner reference after rendering
      if (node && contentDOM && !node.contains(contentDOM)) {
        // @ts-ignore - ignoreMutation may not be declared
        oldIgnoreMutation = this.ignoreMutation; // store ref to previous ignoreMutation

        // ignore all mutations caused by ProseMirror's MutationObserver triggering
        // after DOM change, except selection changes
        // @ts-ignore ProseMirror adds selection type to MutationRecord
        this.ignoreMutation = function (m) {
          var isSelectionMutation = m.type === 'selection';
          if (!isSelectionMutation) {
            mutationsIgnored = true;
          }
          return !isSelectionMutation;
        };

        // capture document selection state before React DOM changes triggers ProseMirror selection change transaction
        if (this.view.state.selection.visible) {
          selectionBookmark = this.view.state.selection.getBookmark();
        }
        node.appendChild(contentDOM);

        // After the next frame:
        requestAnimationFrame(function () {
          // Restore the original mutation handler
          // @ts-ignore - this may not have been declared by implementing class
          _this4.ignoreMutation = oldIgnoreMutation;

          // Restore the selection only if:
          // - We have a selection bookmark
          // - Mutations were ignored during the table move
          // - The bookmarked selection is different from the current selection.
          if (selectionBookmark && mutationsIgnored) {
            var resolvedSelection = selectionBookmark.resolve(_this4.view.state.tr.doc);
            // Don't set the selection if it's the same as the current selection.
            if (!resolvedSelection.eq(_this4.view.state.selection)) {
              var tr = _this4.view.state.tr.setSelection(resolvedSelection);
              tr.setMeta('source', 'ReactNodeView:_handleRef:selection-resync');
              _this4.view.dispatch(tr);
            }
          }
        });
      }
    }

    // Spreading props to pass through dynamic component props
    // Ignored via go/ees005
    // eslint-disable-next-line @typescript-eslint/no-explicit-any
  }, {
    key: "render",
    value: function render(props, forwardRef) {
      return this.reactComponent ? /*#__PURE__*/_react.default.createElement(this.reactComponent, (0, _extends2.default)({
        view: this.view,
        getPos: this.getPos,
        node: this.node,
        forwardRef: forwardRef
        // eslint-disable-next-line react/jsx-props-no-spreading -- Spreading props to pass through dynamic component props
      }, props)) : null;
    }
  }, {
    key: "update",
    value: function update(node, decorations, _innerDecorations) {
      var _this5 = this;
      var validUpdate = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : function () {
        return true;
      };
      // @see https://github.com/ProseMirror/prosemirror/issues/648
      var isValidUpdate = this.node.type === node.type && validUpdate(this.node, node);
      this.decorations = decorations;
      if (!isValidUpdate) {
        return false;
      }
      if (this.domRef && !this.node.sameMarkup(node)) {
        this.setDomAttrs(node, this.domRef);
      }

      // View should not process a re-render if this is false.
      // We dont want to destroy the view, so we return true.
      // TODO: ED-13910 - Fix viewShouldUpdate readonly decoration array
      if (!this.viewShouldUpdate(node, decorations)) {
        this.node = node;
        return true;
      }
      this.node = node;
      this.renderReactComponent(function () {
        return _this5.render(_this5.reactComponentProps, _this5.handleRef);
      });
      return true;
    }
  }, {
    key: "viewShouldUpdate",
    value: function viewShouldUpdate(nextNode, _decorations) {
      if (this._viewShouldUpdate) {
        return this._viewShouldUpdate(nextNode);
      }
      return true;
    }

    /**
     * Copies the attributes from a ProseMirror Node to a DOM node.
     * @param node The Prosemirror Node from which to source the attributes
     */
  }, {
    key: "setDomAttrs",
    value: function setDomAttrs(node, element) {
      Object.keys(node.attrs || {}).forEach(function (attr) {
        element.setAttribute(attr, node.attrs[attr]);
      });
    }
  }, {
    key: "dom",
    get: function get() {
      // Spreading props to pass through dynamic component props
      // Ignored via go/ees005
      // eslint-disable-next-line @atlaskit/editor/no-as-casting
      return this.domRef;
    }
  }, {
    key: "destroy",
    value: function destroy() {
      if (!this.domRef) {
        return;
      }
      this.portalProviderAPI.remove(this.key);
      this.domRef = undefined;
      this.contentDOM = undefined;
    }
  }], [{
    key: "fromComponent",
    value: function fromComponent(
    // Spreading props to pass through dynamic component props
    // Ignored via go/ees005
    // eslint-disable-next-line @typescript-eslint/no-explicit-any
    component, portalProviderAPI, eventDispatcher, props, viewShouldUpdate) {
      return function (node, view, getPos) {
        return new ReactNodeView(node, view, getPos, portalProviderAPI, eventDispatcher, props, component, viewShouldUpdate).init();
      };
    }
  }]);
}();