UNPKG

@atlaskit/editor-common

Version:

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

290 lines (288 loc) • 12.7 kB
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 };