@atlaskit/editor-common
Version:
A package that contains common classes and components for editor and renderer
749 lines (710 loc) • 27 kB
JavaScript
import _slicedToArray from "@babel/runtime/helpers/slicedToArray";
import _construct from "@babel/runtime/helpers/construct";
import _toConsumableArray from "@babel/runtime/helpers/toConsumableArray";
import _classCallCheck from "@babel/runtime/helpers/classCallCheck";
import _createClass from "@babel/runtime/helpers/createClass";
import _defineProperty from "@babel/runtime/helpers/defineProperty";
import { EventDispatcher } from '../event-dispatcher';
/*********************
* *
* BASE TYPES *
* *
**********************/
/**
* 🧱 Internal Type: Editor FE Platform
*
*/
/**
* 🧱 Internal Type: Editor FE Platform
*
*/
/**
* 🧱 Internal Type: Editor FE Platform
*
*/
/****************************************************
* *
* METADATA PROPERTIES EXTRACTION TYPES *
* *
****************************************************/
/**
* 🧱 Internal Type: Editor FE Platform
*
* Extracts the configuration type from a given plugin.
*
*
* @returns The extracted plugin configuration type if applicable, or `never`.
*
* @example
* ```typescript
* type DogPlugin = NextEditorPlugin<'dog'>;
*
* // it returns never, since Dog has no configuration
* type MyPluginConfiguration = ExtractPluginConfiguration<MyPlugin>;
*
*
* type CatPlugin = NextEditorPlugin<'cat', { configuration: { color: 'red' | 'blue' } }>;
*
* // it returns this type { color: 'red' | 'blue' }
* type MyPluginConfiguration = ExtractPluginConfiguration<MyPlugin>;
* ```
*/
/**
* 🧱 Internal Type: Editor FE Platform
*
* Extracts and filters the plugin dependencies from the plugin metadata, excluding
* optional dependencies.
*
* This type first checks if the `dependencies` property in the given `Metadata` type
* is an array of `DependencyPlugin`. If true, it applies `FilterOptionalPlugins` to
* filter out the optional dependencies. If the `dependencies` property does not exist
* or is not an array of `DependencyPlugin`, the type resolves to an empty array.
*
* @returns An array of filtered plugin dependencies or an empty array.
*
* @example
* ```typescript
* type DogPlugin = NextEditorPlugin<'dog'>;
* type LoudPlugin = NextEditorPlugin<'loud'>;
* type BarkMetadata = {dependencies: [
* OptionalPlugin<LoudPlugin>,
* DogPlugin,
* ]}
* type BarkPlugin = NextEditorPlugin<'bark', BarkMetadata>;
*
* // It returns [DogPlugin]
* type RequiredDependencies = ExtractPluginDependenciesFromMetadataWithoutOptionals<BarkMetadata>;
*
* ```
*
* You probably wants to use this other type util @see ExtractPluginDependencies
* since you wouldn't need to infer the Metadata twice
*/
/**
* 🧱 Internal Type: Editor FE Platform
*
* Extracts the plugin configuration from the given plugin metadata if the
* `pluginConfiguration` property exists.
*
* This type conditionally checks if the `Metadata` type includes a `pluginConfiguration`
* key. If such a key exists, the type of `pluginConfiguration` is returned. If not,
* the type resolves to `never`.
*/
/********************************
* *
* TYPE INFER *
* *
*********************************/
/**
* 🧱 Internal Type: Editor FE Platform
*
* Extracts the NextEditorPlugin type from a PresetPuglin,
* this is useful because the EditorPresetBuilder can accept the plugin in multiple ways:
*
* @example
* ```
* preset
* // valid
* .add([plugin, { myConfiguration }] // Type: [NextEditorPlugin, Configuration]
*
* // valid
* .add([plugin]) // Type: [NextEditorPlugin, Configuration?]
*
* // valid
* .add(plugin) // Type: NextEditorPlugin
*
* ```
*
* This type conditionally checks if `Plugin` is an array. If it is an array, it then checks if the first element
* (`MPlugin`) extends `NextEditorPlugin`. But if `Plugin` directly extends `NextEditorPlugin`, it returns the `Plugin`
* type itself. Otherwise, it resolves to `never`.
*
* You probably wants to use this if you need to extract the NextEditorPlugin from a @see PresetPlugin .
* Since the PresetPlugin is an union between a tuple and a plugin.
*/
/**
* 🧱 Internal Type: Editor FE Platform
*
* Extracts non-optional plugin dependencies, excluding any optional dependencies, from a given plugin's metadata.
*
* We can declare the depencies like this:
*
* @example
* ```typescript
* NextEditorPlugin<'bark', {
* dependencies: [DogPlugin, Optional<LoudPlugin>]
* }>
*
* ```
*
*
* This tyope is similar to @see ExtractPluginDependenciesFromMetadataWithoutOptionals
* but you can use it to extract the non-optional-dependencies from any NextEditorPlugin without infer the metadata
*
* @example
* ```typescript
* type BarkPlugin = NextEditorPlugin<'bark', {
* dependencies: [DogPlugin, Optional<LoudPlugin>]
* }>
*
* type PluginDependencies = ExtractPluginDependencies<BarkPlugin>; // Type: [DogPlugin]
* ```
*/
/**
* 🧱 Internal Type: Editor FE Platform
*
* Extracts the NextEditorPlugin type from a PluginWithConfiguration.
*
*
* You probably wants to use this if you need to extract the NextEditorPlugin from a @see PresetPlugin .
* Since the PresetPlugin is an union between a tuple and a plugin.
*/
/**
* 🧱 Internal Type: Editor FE Platform
*
* Extracts the plugin name from a PresetPlugins.
*
* @example
* ```typescript
* ExtractPluginNameFromAllBuilderPlugins<NextEditorPlugin<'bark'>> // 'bark'
*
* ExtractPluginNameFromAllBuilderPlugins<[NextEditorPlugin<'dog'>, { configuration: {} }> // 'dog'
*
* ```
* Similar to @see ExtractPluginAllBuilderPlugins, this type conditionally checks if `Plugin` is an array. If it is,
* it attempts to extract the name of the first plugin (`MPlugin`) in the array that extends `NextEditorPlugin` with
* a name and any metadata. If `Plugin` itself directly extends `NextEditorPlugin`, it extracts the plugin's name.
* If none of these conditions are met, it resolves to `never`.
*
*/
/******************************
* *
* MAPPED TUPLES *
* *
******************************/
/**
* 🧱 Internal Type: Editor FE Platform
*
* Filters out optional plugins from a tuple of dependency plugins.
*
*
* This type is using the Tail Head trick to map a tuple to another one.
* It does this by conditionally iterating over each element in the tuple: if the head of the tuple (the first element)
* is an optional plugin, it is excluded from the resulting tuple; otherwise, it is included. This process is repeated
* for the tail (the remaining elements) of the tuple until all elements have been evaluated.
*
*/
/**
* 🧱 Internal Type: Editor FE Platform
*
* One of the main type system for the EditorPresetBuilder.
*
* Verifies if a given plugin's dependencies are satisfied within a provided stack of plugins.
*
* Usually, the stack of plugins are coming from a generic parameter in the EditorPresetBuilder<PluginNames, PluginStack>.
*
* This type checks if the dependencies of the given `Plugin` are included in the provided `PluginsStack`.
*
* - If the plugin has no dependencies, it simply returns the plugin itself, (provided it is either a `PluginWithConfiguration` or `NextEditorPlugin`, in case someone tries to add a non-NextEditorPlugin to the Preset)
*
* - If the plugin has dependencies, it verifies each dependency against the `PluginsStack` to ensure
* they are present. This includes checking direct dependencies as well as dependencies hidden inside tuples (by unwrapping
* them). If all dependencies are satisfied, it returns the plugin; otherwise, it resolves to `never`.
*
*
* @example
* ```typescript
* type DogPlugin = NextEditorPlugin<'dog'>;
* type LoudPlugin = NextEditorPlugin<'loud'>;
* type BarkPlugin = NextEditorPlugin<'bark', { dependencies: [DogPlugin, LoudPlugin] }>;
*
*
* // When there we are missing dependencies
* VerifyPluginDependencies<BarkPlugin, [DogPlugin]> // Type: never
*
*
* // When there all dependencies are already added on the stack
* VerifyPluginDependencies<BarkPlugin, [DogPlugin, LoudPlugin]> // Type: BarkPlugin
*
* ```
*/
/********************************
* *
* BETTER ERROR MESSAGE TYPES *
* *
*********************************/
/**
* 🧱 Internal Type: Editor FE Platform
*
* TypeScript doesn't allow custom error messages (yet). So, use this type to force a specific error message to the user.
*
* This is useful because in a situation where a Preset has too many plugins, its become really hard to understand what the error message is.
*
* Extracts the names of required dependencies for a given plugin, or provides an error message if dependencies are
* missing, invalid, or if the plugin itself is not a recognized NextEditorPlugin.
*
* This type evaluates whether a given `Plugin` has defined dependencies. If dependencies are absent, it returns
* a message indicating no dependencies were found. If dependencies are present but do not conform to expected types,
* or if an unspecified issue occurs, appropriate error messages are generated. Valid dependencies result in the
* extraction of their names; otherwise, an error message specific to the situation is returned.
*
* It is used by the @see GetDependencyErrorMessage to group all error messages when a new plugin is being added into a preset.
*/
/**
* 🧱 Internal Type: Editor FE Platform
*
* Retrieves an error message if any dependency-related issues are detected for a given plugin within a specified
* plugin stack. This includes missing dependencies or other errors as identified by `ExtractRequiredDependencies`.
*
* It attempts to extract required dependencies for the `Plugin` from the `StackPlugins`. If the result is a string,
* it indicates a missing dependency and constructs an error message accordingly. Otherwise, it directly returns the
* result from `ExtractRequiredDependencies`, which could be an error message detailing the issue encountered.
*
* It is used by the @see SafePresetCheck to make improve the error message
*/
/**
* 🧱 Internal Type: Editor FE Platform
*
* Filters through an array of dependency plugins, removing any that do not exist in the provided plugins stack.
*
* This type recursively checks each plugin dependency against the provided `PluginsStack`. If a dependency is found
* within the stack, it is included in the result; otherwise, it is excluded. This process helps in identifying
* missing plugins from a set of required dependencies.
*
*/
/*****************************
* *
* VALIDATION HELPER TYPES *
* *
******************************/
/**
* 🧱 Internal Type: Editor FE Platform
*
* Checks for duplicate plugin entries within a stack of plugins. If a duplicate is found, it returns an error message;
* otherwise, it proceeds without error.
*
* This type primarily serves to ensure that each plugin in the plugin stack is unique, preventing issues related to
* duplicate plugin registration. It also includes a check to accommodate scenarios where strict typing is bypassed.
*
* If the plugin is used with other configuration this type will not complain.
*/
/**
* 🧱 Internal Type: Editor FE Platform
*
* Verifies if a given plugin meets basic requirements to be considered a valid editor plugin.
*
* This type checks if the plugin is a function that matches the expected signature for an next editor plugin. If it does,
* it further checks the plugin's configuration requirements to ensure compatibility and adherence to expected
* configurations.
*
*/
// Ignored via go/ees005
// eslint-disable-next-line @typescript-eslint/no-explicit-any
/**
* 🧱 Internal Type: Editor FE Platform
*
* Evaluates whether a plugin's configuration meets the requirements to be used either as a standalone plugin or
* as part of a plugin-with-configuration tuple.
*
* This type assesses the plugin configuration's status—whether it's optional, mandatory, or not present—and determines
* the valid ways in which the plugin can be registered or used. This is crucial for maintaining backward compatibility
* and ensuring plugins are correctly configured upon registration into the Preset
*
*/
/*****************************
* *
* EDITOR API HELPER TYPES *
* *
******************************/
/**
* 🧱 Internal Type: Editor FE Platform
*
* Extracts the numeric indices as literal types from a tuple.
*
* This utility type takes a tuple and produces a union of its numeric indices as literal types. It's useful for
* iterating over tuples with TypeScript's mapped types, allowing for operations on each tuple element based on its index.
*
* It is being used to separate plugins registred with `preset.maybeAdd` and `preset.add`.
*/
// Ignored via go/ees005
// eslint-disable-next-line @typescript-eslint/no-explicit-any
/**
* 🧱 Internal Type: Editor FE Platform
*
* Constructs a plugin api type with optional properties based on the optional plugins from a given tuple of plugins.
*
* This type iterates over a tuple of plugins and checks for plugins marked as optional (indicated by the presence
* of `undefined`). For each optional plugin, it attempts to extract the plugin's name and corresponding
* `PluginDependenciesAPI` type. The resulting object type has properties with these plugin names as keys and their
* respective APIs as optional values.
*
* @example
* ```typescript
* type DogPlugin = NextEditorPlugin<'dog'>;
* type CatPlugin = NextEditorPlugin<'cat'>;
*
*
* BuildOptionalAPIEntry<[DogPlugin, MaybePlugin<CatPlugin>]> // Type: { cat?: { } }
*
* ```
*/
/**
* 🧱 Internal Type: Editor FE Platform
* Generates a plugin api type with properties based on the required plugins from a given tuple of plugins.
*
* This type traverses a tuple of plugins, focusing on those not marked as optional. For each required plugin,
* it extracts the plugin's name to use as a key and determines the corresponding `PluginDependenciesAPI` type
* for the value. The resulting object type includes these key-value pairs, ensuring that each required plugin
* has a defined API entry in the object.
*
* @example
* ```typescript
* type DogPlugin = NextEditorPlugin<'dog'>;
* type CatPlugin = NextEditorPlugin<'cat'>;
*
*
* BuildOptionalAPIEntry<[DogPlugin, MaybePlugin<CatPlugin>]> // Type: { dog?: { } }
*
* ```
*/
/**
* 🧱 Internal Type: Editor FE Platform
*
* Forces the expansion (simplification/normalization) of conditional and mapped types.
* This can be particularly useful for making the types more readable and manageable in
* environments like IntelliSense or when generating type documentation.
*
* More info {@link https://github.com/microsoft/TypeScript/issues/47980 TypeScript/issues/47980}
*/
/*************************
* *
* PUBLIC TYPES *
* *
*************************/
/**
* 🧱 Internal Type: Editor FE Platform
*
* Represents a utility type that wraps a string type in a tuple, often used to denote
* plugin names that might be optionally included or excluded in certain contexts within
* the editor preset builder.
*/
/**
* 🧱 Internal Type: Editor FE Platform
*
* A union type that represents a plugin which could either be a standalone `NextEditorPlugin`
* or a `PluginWithConfiguration` that bundles a plugin with its specific configuration.
*
* This type is fundamental in managing plugins within presets, allowing for flexible plugin
* registration that accommodates plugins with or without explicit configurations.
*/
/**
* 🧱 Internal Type: Editor FE Platform
*
* A union type that aggregates all possible plugin name representations within the editor preset builder,
* including simple strings for direct plugin names and wrapped strings in tuples when a plugin is registred with `maybeAdd`.
*
*/
/**
* 🧱 Internal Type: Editor FE Platform
*
* Represents all possible types of plugins that can be included within an editor preset.
* This includes both `PresetPlugin` types and `MaybePlugin` types, accommodating a wide range
* of plugin registration scenarios likw:
*
* @example
* ```typescript
* preset
* .add([plugin, { myConfiguration }]
* .add([plugin])
* .add(plugin)
* .maybeAdd(plugin, () => true);
* .maybeAdd([plugin], () => true);
* .maybeAdd([plugin, { myConfiguration }], () => true);
*
* ```
*/
// Ignored via go/ees005
// eslint-disable-next-line @typescript-eslint/no-explicit-any
/**
* 🧱 Internal Type: Editor FE Platform
*
* Performs a series of checks to ensure that a given plugin can be safely added to a preset.
* This includes verifying the plugin's dependencies, checking for duplicate registrations, and ensuring
* the plugin meets basic criteria for being considered a valid plugin.
*
* @returns The plugin type if all checks pass, or error messages detailing why the plugin cannot be added.
*/
/**
* 📢 Public Type API
*
* Extracts the complete API surface for a given editor preset, including both core and plugin-specific APIs.
* This type dynamically assembles the API object based on the included plugins, differentiating between
* optional and required plugins to accurately reflect the available API calls.
*
* @template Preset The editor preset builder instance from which to extract the API.
* @returns An object type representing the complete API surface for the given preset.
*
* @example
* ```typescript
* const dogPlugin: NextEditorPlugin<'dog'>;
* const catPlugin: NextEditorPlugin<'cat'>;
*
* const myPreset = new EditorPresetBuilder()
* .add(dogPlugin)
* .maybeAdd(catPlugin, () => true)
*
* const api: ExtractPresetAPI<typeof myPreset>;
*
*
* // Core is always available
* api.core.actions
*
* // Dog was registred with `add`, so it will always be available
* api.dog.actions
*
* // Cat was registred with `maybeAdd`, so it may not be available on runtime
* api.cat?.actions
* ```
*/
// Ignored via go/ees005
// eslint-disable-next-line @typescript-eslint/no-explicit-any
/*************************
* *
* PROP TYPES *
* *
*************************/
/**
* This class is the main way to build an Editor.
*
* A Preset is an immutable object, any modification like `.add` or `.maybeAdd`
* will always result in a new preset instance.
*
* ⚠️⚠️⚠️ ATTENTION ⚠️⚠️⚠️
* For ComposableEditor, a new Preset means a full redraw,
* it is one of the most expensive operation.
* Please make sure you aren't recreating this all the time.
*
* EditorAPI:
* In case you need access to the EditorAPI type definition based in the preset you have.
* Please use the util type exported in this package: @see ExtractPresetAPI<Preset>
*
* ```typescript
* const myPreset = new EditorPresetBuilder()
* .add(pluginDog)
* .add(pluginCat);
*
*
* function someFunc(myApi: ExtractPresetAPI<typeof myPreset>) {
*
* }
* ```
*
* If your code is inside an EditorPlugin you should be using the @see ExtractInjectionAPI.
*/
export var EditorPresetBuilder = /*#__PURE__*/function () {
function EditorPresetBuilder() {
var _this = this;
_classCallCheck(this, EditorPresetBuilder);
// eslint-disable-next-line @repo/internal/deprecations/deprecation-ticket-required -- Ignored via go/ED-25883
/**
* @deprecated Use `apiResolver` instead
*/
/**
* Returns the editor API when resolved.
* This occurs when the preset is initially built.
*/
_defineProperty(this, "apiEmitter", function () {});
_defineProperty(this, "safeEntry", function (plugin) {
return Array.isArray(plugin) ? plugin : [plugin, undefined];
});
for (var _len = arguments.length, more = new Array(_len), _key = 0; _key < _len; _key++) {
more[_key] = arguments[_key];
}
this.data = [].concat(more) || [];
this.apiPromise = new Promise(function (r) {
return _this.resolver = r;
});
this.apiResolver = new APIDispatcher(function (emitter) {
return _this.apiEmitter = emitter;
});
}
return _createClass(EditorPresetBuilder, [{
key: "add",
value: function add(nextOrTuple) {
return _construct(EditorPresetBuilder, [
/**
* re-cast this to NewPlugin as we've done all the type
* safety, dependency checking, narrowing, during
* `SafePresetCheck & VerifyPluginDependencies`
*/
nextOrTuple].concat(_toConsumableArray(this.data)));
}
}, {
key: "maybeAdd",
value: function maybeAdd(pluginToAdd, shouldAdd) {
var pluginOrBuilder = typeof shouldAdd === 'function' ?
// @ts-expect-error Argument of type 'SafePresetCheck<ToAddPlugin, StackPlugins>' is not assignable to parameter of type 'ToAddPlugin'.
shouldAdd(pluginToAdd, this) : shouldAdd;
if (pluginOrBuilder instanceof EditorPresetBuilder) {
return pluginOrBuilder;
}
var nextPluginStack = [
/**
* re-cast this to NewPlugin as we've done all the type
* safety, dependency checking, narrowing, during
* `SafePresetCheck & VerifyPluginDependencies`
*/
pluginOrBuilder ? pluginToAdd : undefined].concat(_toConsumableArray(this.data));
var nextEditorPresetBuilder = _construct(EditorPresetBuilder, _toConsumableArray(nextPluginStack));
return nextEditorPresetBuilder;
}
}, {
key: "has",
value: function has(plugin) {
return this.data.some(function (pluginPreset) {
if (Array.isArray(pluginPreset)) {
return pluginPreset[0] === plugin;
}
return pluginPreset === plugin;
});
}
}, {
key: "build",
value: function build() {
var _ref = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {},
pluginInjectionAPI = _ref.pluginInjectionAPI,
maybeExcludePlugins = _ref.excludePlugins;
var excludePlugins = new Set(maybeExcludePlugins ? maybeExcludePlugins : []);
var editorPlugins = this.processEditorPlugins({
pluginInjectionAPI: pluginInjectionAPI,
excludePlugins: excludePlugins
});
if (pluginInjectionAPI) {
var _this$resolver;
// The pluginInjectionAPI API doesn't have information enough to build a proper type for the API.
// It is returning a generic type but on top of the Proxy system
// So, we can safely recast it here
this.apiEmitter(pluginInjectionAPI.api());
// Deprecated approach
(_this$resolver = this.resolver) === null || _this$resolver === void 0 || _this$resolver.call(this, pluginInjectionAPI.api());
}
return this.removeExcludedPlugins(editorPlugins, excludePlugins);
}
}, {
key: "verifyDuplicatedPlugins",
value: function verifyDuplicatedPlugins() {
var cache = new Set();
this.data.filter(Boolean).forEach(function (pluginEntry) {
var _ref2 = Array.isArray(pluginEntry) ? pluginEntry : [pluginEntry, undefined],
_ref3 = _slicedToArray(_ref2, 2),
pluginFn = _ref3[0],
_ = _ref3[1];
if (cache.has(pluginFn)) {
throw new Error("".concat(pluginFn, " is already included!"));
}
cache.add(pluginFn);
});
return true;
}
}, {
key: "processEditorPlugins",
value: function processEditorPlugins(_ref4) {
var _this2 = this;
var pluginInjectionAPI = _ref4.pluginInjectionAPI,
excludePlugins = _ref4.excludePlugins;
this.verifyDuplicatedPlugins();
var seen = new Set();
var pluginsDataCopy = this.data.slice();
var plugins = pluginsDataCopy.reverse().filter(Boolean).map(function (entry) {
var _this2$safeEntry = _this2.safeEntry(entry),
_this2$safeEntry2 = _slicedToArray(_this2$safeEntry, 2),
fn = _this2$safeEntry2[0],
config = _this2$safeEntry2[1];
if (seen.has(fn)) {
return null;
}
seen.add(fn);
if (typeof fn !== 'function') {
return null;
}
var plugin = pluginInjectionAPI ? fn({
config: config,
api: pluginInjectionAPI.api()
}) : fn({
config: config
});
if (plugin && excludePlugins !== null && excludePlugins !== void 0 && excludePlugins.has(plugin.name)) {
return null;
}
if (!pluginInjectionAPI) {
return plugin;
}
pluginInjectionAPI.onEditorPluginInitialized(plugin);
return plugin;
}).filter(Boolean);
return plugins;
}
}, {
key: "removeExcludedPlugins",
value: function removeExcludedPlugins(plugins, excludes) {
if (excludes) {
return plugins.filter(function (plugin) {
return !plugin || !excludes.has(plugin.name);
});
}
return plugins;
}
}]);
}();
/**
* WARNING: Internal object
*
* Dedicated wrapper around EventDispatcher for public API around the editor API.
* This only has the public method `on` which is used to listen to updates to the editor API.
*
* This shouldn't really be used externally - the `editorAPI` should be accessed via `usePreset`.
*/
var APIDispatcher = /*#__PURE__*/function () {
function APIDispatcher(emitter) {
var _this3 = this;
_classCallCheck(this, APIDispatcher);
_defineProperty(this, "eventDispatcher", new EventDispatcher());
_defineProperty(this, "key", 'api-resolved-event');
// Need to store the last event created in case we subscribe after the fact
_defineProperty(this, "initialEvent", undefined);
this.emitter = emitter;
this.emitter(function (v) {
_this3.initialEvent = v;
_this3.eventDispatcher.emit(_this3.key, v);
});
}
/**
* Used to observe Editor API events
*
* @param cb Callback to listen to the editor API.
* This will also emit the last event if the stream has already started.
* @returns Cleanup function to cleanup the listener
*/
return _createClass(APIDispatcher, [{
key: "on",
value: function on(cb) {
var _this4 = this;
if (this.initialEvent !== undefined) {
// Keeps timing consistent with old approach - certain products
// ie. ai-mate relied on this.
Promise.resolve().then(function () {
return cb(_this4.initialEvent);
});
}
this.eventDispatcher.on(this.key, cb);
return function () {
_this4.eventDispatcher.off(_this4.key, cb);
};
}
}, {
key: "destroy",
value: function destroy() {
this.eventDispatcher.destroy();
}
}]);
}();