@atlaskit/editor-plugin-undo-redo
Version:
Undo redo plugin for @atlaskit/editor-core
91 lines (90 loc) • 2.76 kB
JavaScript
import { ACTION_SUBJECT, EVENT_TYPE, INPUT_METHOD } from '@atlaskit/editor-common/analytics';
import { withAnalytics } from '@atlaskit/editor-common/editor-analytics';
import { areNodesEqualIgnoreAttrs } from '@atlaskit/editor-common/utils/document';
import { InputSource } from './enums';
import { pluginKey as undoPluginKey } from './plugin-key';
export const attachInputMeta = inputSource => command => (state, dispatch) => {
let customTr = state.tr;
const fakeDispatch = tr => {
customTr = tr;
};
command(state, fakeDispatch);
if (!customTr || !customTr.docChanged) {
return false;
}
customTr.setMeta(undoPluginKey, inputSource);
if (dispatch) {
dispatch(customTr);
}
return true;
};
const inputSourceToInputMethod = inputSource => {
switch (inputSource) {
case InputSource.EXTERNAL:
return INPUT_METHOD.EXTERNAL;
case InputSource.KEYBOARD:
return INPUT_METHOD.KEYBOARD;
case InputSource.TOOLBAR:
return INPUT_METHOD.TOOLBAR;
default:
return INPUT_METHOD.EXTERNAL;
}
};
function getNodesWithDifferingAttributes({
before,
after
}) {
const allAttributeKeys = Object.keys({
...before.attrs,
...after.attrs
});
const differingAttributes = allAttributeKeys.filter(key => before.attrs[key] !== after.attrs[key]);
const affectedNodes = differingAttributes.length ? [{
type: before.type.name,
attributes: differingAttributes
}] : [];
return before.children.reduce((acc, beforeChild, index) => [...acc, ...getNodesWithDifferingAttributes({
before: beforeChild,
after: after.child(index)
})], affectedNodes);
}
/**
* Analyzes changes between two ProseMirror document nodes.
*
* @param params - Object containing before and after node states
* @param params.before - The document node before changes
* @param params.after - The document node after changes
* @returns Object containing change analysis results with hasChanged boolean and affectedNodes array
*/
export function getChanges({
before,
after
}) {
const hasChanged = !areNodesEqualIgnoreAttrs(after, before);
const affectedNodes = hasChanged ? undefined : getNodesWithDifferingAttributes({
before,
after
})
// Limit to 25 nodes to avoid oversize payloads
.slice(0, 25);
return {
hasChanged,
affectedNodes
};
}
export const attachInputMetaWithAnalytics = editorAnalyticsAPI => (inputSource, action) => command => attachInputMeta(inputSource)(withAnalytics(editorAnalyticsAPI, ({
doc: before
}, {
currentDoc: after
}) => ({
eventType: EVENT_TYPE.TRACK,
action,
actionSubject: ACTION_SUBJECT.EDITOR,
attributes: {
inputMethod: inputSourceToInputMethod(inputSource),
...getChanges({
before,
after
})
}
}))(command));