@atlaskit/editor-common
Version:
A package that contains common classes and components for editor and renderer
228 lines (225 loc) • 10 kB
JavaScript
import _slicedToArray from "@babel/runtime/helpers/slicedToArray";
import _classCallCheck from "@babel/runtime/helpers/classCallCheck";
import _createClass from "@babel/runtime/helpers/createClass";
import _assertThisInitialized from "@babel/runtime/helpers/assertThisInitialized";
import _inherits from "@babel/runtime/helpers/inherits";
import _possibleConstructorReturn from "@babel/runtime/helpers/possibleConstructorReturn";
import _getPrototypeOf from "@babel/runtime/helpers/getPrototypeOf";
import _defineProperty from "@babel/runtime/helpers/defineProperty";
var _class4;
function _createSuper(Derived) { var hasNativeReflectConstruct = _isNativeReflectConstruct(); return function _createSuperInternal() { var Super = _getPrototypeOf(Derived), result; if (hasNativeReflectConstruct) { var NewTarget = _getPrototypeOf(this).constructor; result = Reflect.construct(Super, arguments, NewTarget); } else { result = Super.apply(this, arguments); } return _possibleConstructorReturn(this, result); }; }
function _isNativeReflectConstruct() { try { var t = !Boolean.prototype.valueOf.call(Reflect.construct(Boolean, [], function () {})); } catch (t) {} return (_isNativeReflectConstruct = function _isNativeReflectConstruct() { return !!t; })(); }
import React from 'react';
import PropTypes from 'prop-types';
import { createPortal, unmountComponentAtNode, unstable_renderSubtreeIntoContainer } from 'react-dom';
import { injectIntl, RawIntlProvider, useIntl } from 'react-intl-next';
import { default as AnalyticsReactContext } from '@atlaskit/analytics-next-stable-react-context';
import { ACTION, ACTION_SUBJECT, ACTION_SUBJECT_ID, EVENT_TYPE } from '../../analytics';
import { EventDispatcher } from '../../event-dispatcher';
import IntlProviderIfMissingWrapper from '../IntlProviderIfMissingWrapper';
export var PortalProviderAPI = /*#__PURE__*/function (_EventDispatcher) {
_inherits(PortalProviderAPI, _EventDispatcher);
var _super = _createSuper(PortalProviderAPI);
function PortalProviderAPI(intl, onAnalyticsEvent, analyticsContext) {
var _this;
_classCallCheck(this, PortalProviderAPI);
_this = _super.call(this);
_defineProperty(_assertThisInitialized(_this), "portals", new Map());
_defineProperty(_assertThisInitialized(_this), "setContext", function (context) {
_this.context = context;
});
_this.intl = intl;
_this.onAnalyticsEvent = onAnalyticsEvent;
_this.useAnalyticsContext = analyticsContext;
return _this;
}
_createClass(PortalProviderAPI, [{
key: "render",
value: function render(children, container) {
var hasAnalyticsContext = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false;
var hasIntlContext = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : false;
this.portals.set(container, {
children: children,
hasAnalyticsContext: hasAnalyticsContext,
hasIntlContext: hasIntlContext
});
var wrappedChildren = this.useAnalyticsContext ? /*#__PURE__*/React.createElement(AnalyticsContextWrapper, null, children()) : children();
if (hasIntlContext) {
wrappedChildren = /*#__PURE__*/React.createElement(RawIntlProvider, {
value: this.intl
}, wrappedChildren);
}
unstable_renderSubtreeIntoContainer(this.context, wrappedChildren, container);
}
// TODO: until https://product-fabric.atlassian.net/browse/ED-5013
// we (unfortunately) need to re-render to pass down any updated context.
// selectively do this for nodeviews that opt-in via `hasAnalyticsContext`
}, {
key: "forceUpdate",
value: function forceUpdate(_ref) {
var _this2 = this;
var intl = _ref.intl;
this.intl = intl;
this.portals.forEach(function (portal, container) {
if (!portal.hasAnalyticsContext && !_this2.useAnalyticsContext && !portal.hasIntlContext) {
return;
}
var wrappedChildren = portal.children();
if (portal.hasAnalyticsContext && _this2.useAnalyticsContext) {
wrappedChildren = /*#__PURE__*/React.createElement(AnalyticsContextWrapper, null, wrappedChildren);
}
if (portal.hasIntlContext) {
wrappedChildren = /*#__PURE__*/React.createElement(RawIntlProvider, {
value: _this2.intl
}, wrappedChildren);
}
unstable_renderSubtreeIntoContainer(_this2.context, wrappedChildren, container);
});
}
}, {
key: "remove",
value: function remove(container) {
this.portals.delete(container);
// There is a race condition that can happen caused by Prosemirror vs React,
// where Prosemirror removes the container from the DOM before React gets
// around to removing the child from the container
// This will throw a NotFoundError: The node to be removed is not a child of this node
// Both Prosemirror and React remove the elements asynchronously, and in edge
// cases Prosemirror beats React
try {
unmountComponentAtNode(container);
} catch (error) {
if (this.onAnalyticsEvent) {
this.onAnalyticsEvent({
payload: {
action: ACTION.FAILED_TO_UNMOUNT,
actionSubject: ACTION_SUBJECT.EDITOR,
actionSubjectId: ACTION_SUBJECT_ID.REACT_NODE_VIEW,
attributes: {
error: error,
domNodes: {
container: container ? container.className : undefined,
child: container.firstElementChild ? container.firstElementChild.className : undefined
}
},
eventType: EVENT_TYPE.OPERATIONAL
}
});
}
}
}
}]);
return PortalProviderAPI;
}(EventDispatcher);
var BasePortalProvider = /*#__PURE__*/function (_React$Component) {
_inherits(BasePortalProvider, _React$Component);
var _super2 = _createSuper(BasePortalProvider);
function BasePortalProvider(props) {
var _this3;
_classCallCheck(this, BasePortalProvider);
_this3 = _super2.call(this, props);
_this3.portalProviderAPI = new PortalProviderAPI(props.intl, props.onAnalyticsEvent, props.useAnalyticsContext);
return _this3;
}
_createClass(BasePortalProvider, [{
key: "render",
value: function render() {
return this.props.render(this.portalProviderAPI);
}
}, {
key: "componentDidUpdate",
value: function componentDidUpdate() {
this.portalProviderAPI.forceUpdate({
intl: this.props.intl
});
}
}]);
return BasePortalProvider;
}(React.Component);
_defineProperty(BasePortalProvider, "displayName", 'PortalProvider');
export var PortalProvider = injectIntl(BasePortalProvider);
export var PortalProviderWithThemeProviders = function PortalProviderWithThemeProviders(_ref2) {
var onAnalyticsEvent = _ref2.onAnalyticsEvent,
useAnalyticsContext = _ref2.useAnalyticsContext,
render = _ref2.render;
return /*#__PURE__*/React.createElement(IntlProviderIfMissingWrapper, null, /*#__PURE__*/React.createElement(PortalProviderWithThemeAndIntlProviders, {
onAnalyticsEvent: onAnalyticsEvent,
useAnalyticsContext: useAnalyticsContext,
render: render
}));
};
var PortalProviderWithThemeAndIntlProviders = function PortalProviderWithThemeAndIntlProviders(_ref3) {
var onAnalyticsEvent = _ref3.onAnalyticsEvent,
useAnalyticsContext = _ref3.useAnalyticsContext,
render = _ref3.render;
var intl = useIntl();
return /*#__PURE__*/React.createElement(BasePortalProvider, {
intl: intl,
onAnalyticsEvent: onAnalyticsEvent,
useAnalyticsContext: useAnalyticsContext,
render: render
});
};
export var PortalRenderer = /*#__PURE__*/function (_React$Component2) {
_inherits(PortalRenderer, _React$Component2);
var _super3 = _createSuper(PortalRenderer);
function PortalRenderer(props) {
var _this4;
_classCallCheck(this, PortalRenderer);
_this4 = _super3.call(this, props);
_defineProperty(_assertThisInitialized(_this4), "handleUpdate", function (portals) {
return _this4.setState({
portals: portals
});
});
props.portalProviderAPI.setContext(_assertThisInitialized(_this4));
props.portalProviderAPI.on('update', _this4.handleUpdate);
_this4.state = {
portals: new Map()
};
return _this4;
}
_createClass(PortalRenderer, [{
key: "render",
value: function render() {
var portals = this.state.portals;
return /*#__PURE__*/React.createElement(React.Fragment, null, Array.from(portals.entries()).map(function (_ref4) {
var _ref5 = _slicedToArray(_ref4, 2),
container = _ref5[0],
children = _ref5[1];
return /*#__PURE__*/createPortal(children, container);
}));
}
}]);
return PortalRenderer;
}(React.Component);
/**
* Wrapper to re-provide modern analytics context to ReactNodeViews.
*/
var dummyAnalyticsContext = {
getAtlaskitAnalyticsContext: function getAtlaskitAnalyticsContext() {},
getAtlaskitAnalyticsEventHandlers: function getAtlaskitAnalyticsEventHandlers() {}
};
var AnalyticsContextWrapper = (_class4 = /*#__PURE__*/function (_React$Component3) {
_inherits(AnalyticsContextWrapper, _React$Component3);
var _super4 = _createSuper(AnalyticsContextWrapper);
function AnalyticsContextWrapper() {
_classCallCheck(this, AnalyticsContextWrapper);
return _super4.apply(this, arguments);
}
_createClass(AnalyticsContextWrapper, [{
key: "render",
value: function render() {
var _ref6 = this.context.contextAdapter.analytics || {
value: dummyAnalyticsContext
},
value = _ref6.value;
return /*#__PURE__*/React.createElement(AnalyticsReactContext.Provider, {
value: value
}, this.props.children);
}
}]);
return AnalyticsContextWrapper;
}(React.Component), _defineProperty(_class4, "contextTypes", {
contextAdapter: PropTypes.object
}), _class4);