UNPKG

@atlaskit/editor-common

Version:

A package that contains common classes and components for editor and renderer

296 lines (293 loc) • 13.6 kB
"use strict"; var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault"); Object.defineProperty(exports, "__esModule", { value: true }); exports.WithPluginState = void 0; var _classCallCheck2 = _interopRequireDefault(require("@babel/runtime/helpers/classCallCheck")); var _createClass2 = _interopRequireDefault(require("@babel/runtime/helpers/createClass")); var _assertThisInitialized2 = _interopRequireDefault(require("@babel/runtime/helpers/assertThisInitialized")); var _inherits2 = _interopRequireDefault(require("@babel/runtime/helpers/inherits")); var _possibleConstructorReturn2 = _interopRequireDefault(require("@babel/runtime/helpers/possibleConstructorReturn")); var _getPrototypeOf2 = _interopRequireDefault(require("@babel/runtime/helpers/getPrototypeOf")); var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty")); var _react = _interopRequireDefault(require("react")); var _propTypes = _interopRequireDefault(require("prop-types")); var _analytics = require("../analytics"); var _eventDispatcher = require("../event-dispatcher"); var _utils = require("../utils"); 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) { (0, _defineProperty2.default)(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 = (0, _getPrototypeOf2.default)(Derived), result; if (hasNativeReflectConstruct) { var NewTarget = (0, _getPrototypeOf2.default)(this).constructor; result = Reflect.construct(Super, arguments, NewTarget); } else { result = Super.apply(this, arguments); } return (0, _possibleConstructorReturn2.default)(this, result); }; } function _isNativeReflectConstruct() { try { var t = !Boolean.prototype.valueOf.call(Reflect.construct(Boolean, [], function () {})); } catch (t) {} return (_isNativeReflectConstruct = function _isNativeReflectConstruct() { return !!t; })(); } 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 = exports.WithPluginState = /*#__PURE__*/function (_React$Component) { (0, _inherits2.default)(WithPluginState, _React$Component); var _super = _createSuper(WithPluginState); function WithPluginState(props, context) { var _this; (0, _classCallCheck2.default)(this, WithPluginState); _this = _super.call(this, props, context); (0, _defineProperty2.default)((0, _assertThisInitialized2.default)(_this), "listeners", {}); (0, _defineProperty2.default)((0, _assertThisInitialized2.default)(_this), "debounce", null); (0, _defineProperty2.default)((0, _assertThisInitialized2.default)(_this), "notAppliedState", {}); (0, _defineProperty2.default)((0, _assertThisInitialized2.default)(_this), "isSubscribed", false); (0, _defineProperty2.default)((0, _assertThisInitialized2.default)(_this), "callsCount", 0); (0, _defineProperty2.default)((0, _assertThisInitialized2.default)(_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: (0, _defineProperty2.default)({}, propName, pluginState), pluginName: pluginName, performanceOptions: performanceOptions }); } }; }); /** * Debounces setState calls in order to reduce number of re-renders caused by several plugin state changes. */ (0, _defineProperty2.default)((0, _assertThisInitialized2.default)(_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 && (0, _utils.startMeasure)(measure); _this.setState(_this.notAppliedState, function () { performanceOptions.trackingEnabled && (0, _utils.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: _analytics.ACTION.WITH_PLUGIN_STATE_CALLED, actionSubject: _analytics.ACTION_SUBJECT.EDITOR, eventType: _analytics.EVENT_TYPE.OPERATIONAL, attributes: { plugin: pluginName, duration: duration } }); } }); }); _this.debounce = null; _this.notAppliedState = {}; }); }); (0, _defineProperty2.default)((0, _assertThisInitialized2.default)(_this), "dispatchAnalyticsEvent", function (payload) { var eventDispatcher = _this.getEventDispatcher(); if (eventDispatcher) { var dispatch = (0, _eventDispatcher.createDispatch)(eventDispatcher); dispatch(_utils.analyticsEventKey, { payload: payload }); } }); (0, _defineProperty2.default)((0, _assertThisInitialized2.default)(_this), "onContextUpdate", function () { _this.subscribe(_this.props); }); _this.state = _this.getPluginsStates(_this.props.plugins, _this.getEditorView(props, context)); return _this; } (0, _createClass2.default)(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.default.Component); (0, _defineProperty2.default)(WithPluginState, "displayName", 'WithPluginState'); (0, _defineProperty2.default)(WithPluginState, "contextTypes", { editorActions: _propTypes.default.object, editorSharedConfig: _propTypes.default.object });