@atlaskit/editor-common
Version:
A package that contains common classes and components for editor and renderer
290 lines (288 loc) • 12.7 kB
JavaScript
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";
function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
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 { ACTION, ACTION_SUBJECT, EVENT_TYPE } from '../analytics';
import { createDispatch } from '../event-dispatcher';
import { analyticsEventKey, startMeasure, stopMeasure } from '../utils';
var DEFAULT_SAMPLING_RATE = 100;
var DEFAULT_SLOW_THRESHOLD = 4;
// That context was exctract from the old WithPluginState from editor-core
// It was using some private types from
// - EditorAction: packages/editor/editor-core/src/actions/index.ts
// - EditorSharedConfig: packages/editor/editor-core/src/labs/next/internal/context/shared-config.tsx
/**
* @private
* @deprecated
*
* Using this component is deprecated. It should be replaced with `useSharedPluginState`.
* This requires having access to the injection API from the plugin itself.
*
* An example of the refactor with the new hook (using hyperlink as an example) is:
*
* Before:
* ```ts
* <WithPluginState
* editorView={editorView}
* plugins={{
* hyperlinkState: hyperlinkPluginKey
* }}
* render={({ hyperlinkState }) =>
* renderComponent({ hyperlinkState })
* }
* />
* ```
*
* After:
* ```ts
* import { useSharedPluginState } from '@atlaskit/editor-common/hooks';
* import type { ExtractInjectionAPI } from '@atlaskit/editor-common/types';
*
* function ComponentWithState(
* api: ExtractInjectionAPI<typeof hyperlinkPlugin> | undefined
* ) {
* const { hyperlinkState } = useSharedPluginState(api, ['hyperlink']);
* return renderComponent({ hyperlinkState })
* }
* ```
*
*/
var WithPluginState = /*#__PURE__*/function (_React$Component) {
_inherits(WithPluginState, _React$Component);
var _super = _createSuper(WithPluginState);
function WithPluginState(props, context) {
var _this;
_classCallCheck(this, WithPluginState);
_this = _super.call(this, props, context);
_defineProperty(_assertThisInitialized(_this), "listeners", {});
_defineProperty(_assertThisInitialized(_this), "debounce", null);
_defineProperty(_assertThisInitialized(_this), "notAppliedState", {});
_defineProperty(_assertThisInitialized(_this), "isSubscribed", false);
_defineProperty(_assertThisInitialized(_this), "callsCount", 0);
_defineProperty(_assertThisInitialized(_this), "handlePluginStateChange", function (propName, pluginName, performanceOptions, skipEqualityCheck) {
return function (pluginState) {
// skipEqualityCheck is being used for old plugins since they are mutating plugin state instead of creating a new one
if (_this.state[propName] !== pluginState || skipEqualityCheck) {
_this.updateState({
stateSubset: _defineProperty({}, propName, pluginState),
pluginName: pluginName,
performanceOptions: performanceOptions
});
}
};
});
/**
* Debounces setState calls in order to reduce number of re-renders caused by several plugin state changes.
*/
_defineProperty(_assertThisInitialized(_this), "updateState", function (_ref) {
var stateSubset = _ref.stateSubset,
pluginName = _ref.pluginName,
performanceOptions = _ref.performanceOptions;
_this.notAppliedState = _objectSpread(_objectSpread({}, _this.notAppliedState), stateSubset);
if (_this.debounce) {
window.clearTimeout(_this.debounce);
}
var debounce = _this.props.debounce !== false ? function (fn) {
return window.setTimeout(fn, 0);
} : function (fn) {
return fn();
};
_this.debounce = debounce(function () {
var measure = "\uD83E\uDD89".concat(pluginName, "::WithPluginState");
performanceOptions.trackingEnabled && startMeasure(measure);
_this.setState(_this.notAppliedState, function () {
performanceOptions.trackingEnabled && stopMeasure(measure, function (duration) {
// Each WithPluginState component will fire analytics event no more than once every `samplingLimit` times
if (++_this.callsCount % performanceOptions.samplingRate === 0 && duration > performanceOptions.slowThreshold) {
_this.dispatchAnalyticsEvent({
action: ACTION.WITH_PLUGIN_STATE_CALLED,
actionSubject: ACTION_SUBJECT.EDITOR,
eventType: EVENT_TYPE.OPERATIONAL,
attributes: {
plugin: pluginName,
duration: duration
}
});
}
});
});
_this.debounce = null;
_this.notAppliedState = {};
});
});
_defineProperty(_assertThisInitialized(_this), "dispatchAnalyticsEvent", function (payload) {
var eventDispatcher = _this.getEventDispatcher();
if (eventDispatcher) {
var dispatch = createDispatch(eventDispatcher);
dispatch(analyticsEventKey, {
payload: payload
});
}
});
_defineProperty(_assertThisInitialized(_this), "onContextUpdate", function () {
_this.subscribe(_this.props);
});
_this.state = _this.getPluginsStates(_this.props.plugins, _this.getEditorView(props, context));
return _this;
}
_createClass(WithPluginState, [{
key: "getEditorView",
value: function getEditorView(maybeProps, maybeContext) {
var props = maybeProps || this.props;
var context = maybeContext || this.context;
return props.editorView || context && context.editorActions && context.editorActions._privateGetEditorView() || context && context.editorSharedConfig && context.editorSharedConfig.editorView;
}
}, {
key: "getEventDispatcher",
value: function getEventDispatcher(maybeProps) {
var props = maybeProps || this.props;
return props.eventDispatcher || this.context && this.context.editorActions && this.context.editorActions._privateGetEventDispatcher() || this.context && this.context.editorSharedConfig && this.context.editorSharedConfig.eventDispatcher;
}
}, {
key: "getPluginsStates",
value: function getPluginsStates(plugins, editorView) {
if (!editorView || !plugins) {
return {};
}
var keys = Object.keys(plugins);
return keys.reduce(function (acc, propName) {
var pluginKey = plugins[propName];
if (!pluginKey) {
return acc;
}
acc[propName] = pluginKey.getState(editorView.state);
return acc;
}, {});
}
}, {
key: "subscribe",
value: function subscribe(props) {
var _uiTracking$samplingR,
_uiTracking$slowThres,
_this2 = this;
var plugins = props.plugins;
var eventDispatcher = this.getEventDispatcher(props);
var editorView = this.getEditorView(props);
if (!eventDispatcher || !editorView || this.isSubscribed) {
return;
}
// TODO: ED-15663
// Please, do not copy or use this kind of code below
// @ts-ignore
var fakePluginKey = {
key: 'analyticsPlugin$',
getState: function getState(state) {
return state['analyticsPlugin$'];
}
};
var analyticsPlugin = fakePluginKey.getState(editorView.state);
var uiTracking = analyticsPlugin && analyticsPlugin.performanceTracking ? analyticsPlugin.performanceTracking.uiTracking || {} : {};
var trackingEnabled = uiTracking.enabled === true;
var samplingRate = (_uiTracking$samplingR = uiTracking.samplingRate) !== null && _uiTracking$samplingR !== void 0 ? _uiTracking$samplingR : DEFAULT_SAMPLING_RATE;
var slowThreshold = (_uiTracking$slowThres = uiTracking.slowThreshold) !== null && _uiTracking$slowThres !== void 0 ? _uiTracking$slowThres : DEFAULT_SLOW_THRESHOLD;
this.isSubscribed = true;
var pluginsStates = this.getPluginsStates(plugins, editorView);
this.setState(pluginsStates);
Object.keys(plugins).forEach(function (propName) {
var pluginKey = plugins[propName];
if (!pluginKey) {
return;
}
var pluginName = pluginKey.key;
var pluginState = pluginsStates[propName];
var isPluginWithSubscribe = pluginState && pluginState.subscribe;
var handler = _this2.handlePluginStateChange(propName, pluginName, {
samplingRate: samplingRate,
slowThreshold: slowThreshold,
trackingEnabled: trackingEnabled
}, isPluginWithSubscribe);
if (isPluginWithSubscribe) {
pluginState.subscribe(handler);
} else {
eventDispatcher.on(pluginKey.key, handler);
}
_this2.listeners[pluginKey.key] = {
handler: handler,
pluginKey: pluginKey
};
});
}
}, {
key: "unsubscribe",
value: function unsubscribe() {
var _this3 = this;
var eventDispatcher = this.getEventDispatcher();
var editorView = this.getEditorView();
if (!eventDispatcher || !editorView || !this.isSubscribed) {
return;
}
Object.keys(this.listeners).forEach(function (key) {
var pluginState = _this3.listeners[key].pluginKey.getState(editorView.state);
if (pluginState && pluginState.unsubscribe) {
pluginState.unsubscribe(_this3.listeners[key].handler);
} else {
eventDispatcher.off(key, _this3.listeners[key].handler);
}
});
this.listeners = [];
}
}, {
key: "subscribeToContextUpdates",
value: function subscribeToContextUpdates(context) {
if (context && context.editorActions) {
context.editorActions._privateSubscribe(this.onContextUpdate);
}
}
}, {
key: "unsubscribeFromContextUpdates",
value: function unsubscribeFromContextUpdates(context) {
if (context && context.editorActions) {
context.editorActions._privateUnsubscribe(this.onContextUpdate);
}
}
}, {
key: "componentDidMount",
value: function componentDidMount() {
this.subscribe(this.props);
this.subscribeToContextUpdates(this.context);
}
}, {
key: "UNSAFE_componentWillReceiveProps",
value: function UNSAFE_componentWillReceiveProps(nextProps) {
if (!this.isSubscribed) {
this.subscribe(nextProps);
}
}
}, {
key: "componentWillUnmount",
value: function componentWillUnmount() {
if (this.debounce) {
window.clearTimeout(this.debounce);
}
this.unsubscribeFromContextUpdates(this.context);
this.unsubscribe();
}
}, {
key: "render",
value: function render() {
var render = this.props.render;
return render(this.state);
}
}]);
return WithPluginState;
}(React.Component);
_defineProperty(WithPluginState, "displayName", 'WithPluginState');
_defineProperty(WithPluginState, "contextTypes", {
editorActions: PropTypes.object,
editorSharedConfig: PropTypes.object
});
export { WithPluginState };