@atlaskit/editor-plugin-show-diff
Version:
ShowDiff plugin for @atlaskit/editor-core
172 lines (171 loc) • 10 kB
JavaScript
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; }
import { processRawValue } from '@atlaskit/editor-common/process-raw-value';
import { SafePlugin } from '@atlaskit/editor-common/safe-plugin';
import { PluginKey } from '@atlaskit/editor-prosemirror/state';
import { Step as ProseMirrorStep } from '@atlaskit/editor-prosemirror/transform';
import { DecorationSet } from '@atlaskit/editor-prosemirror/view';
import { fg } from '@atlaskit/platform-feature-flags';
import { expValEquals } from '@atlaskit/tmp-editor-statsig/exp-val-equals';
import { calculateDiffDecorations } from './calculateDiff/calculateDiffDecorations';
import { enforceCustomStepRegisters } from './enforceCustomStepRegisters';
import { getScrollableDecorations } from './getScrollableDecorations';
import { NodeViewSerializer } from './NodeViewSerializer';
import { scrollToActiveDecoration } from './scrollToActiveDecoration';
export var showDiffPluginKey = new PluginKey('showDiffPlugin');
export var createPlugin = function createPlugin(config, getIntl, api) {
if (fg('platform_editor_show_diff_equality_fallback')) {
enforceCustomStepRegisters();
}
var nodeViewSerializer = new NodeViewSerializer({});
var setNodeViewSerializer = function setNodeViewSerializer(editorView) {
nodeViewSerializer.init({
editorView: editorView
});
};
return new SafePlugin({
key: showDiffPluginKey,
state: {
init: function init(_, _state) {
// We do initial setup after we setup the editor view
return _objectSpread({
steps: [],
originalDoc: undefined,
decorations: DecorationSet.empty,
isDisplayingChanges: false
}, expValEquals('platform_editor_diff_plugin_extended', 'isEnabled', true) ? {
isInverted: false,
diffType: 'inline'
} : {});
},
apply: function apply(tr, currentPluginState, oldState, newState) {
var meta = tr.getMeta(showDiffPluginKey);
var newPluginState = currentPluginState;
if (meta) {
if ((meta === null || meta === void 0 ? void 0 : meta.action) === 'SHOW_DIFF') {
var _newPluginState, _newPluginState2;
// Update the plugin state with the new metadata
newPluginState = _objectSpread(_objectSpread(_objectSpread({}, currentPluginState), meta), {}, {
isDisplayingChanges: true,
activeIndex: undefined
});
// Calculate and store decorations in state
var decorations = calculateDiffDecorations(_objectSpread({
state: newState,
pluginState: newPluginState,
nodeViewSerializer: nodeViewSerializer,
colorScheme: config === null || config === void 0 ? void 0 : config.colorScheme,
intl: getIntl(),
activeIndexPos: fg('platform_editor_show_diff_scroll_navigation') ? newPluginState.activeIndexPos : undefined,
api: api
}, expValEquals('platform_editor_diff_plugin_extended', 'isEnabled', true) ? {
isInverted: (_newPluginState = newPluginState) === null || _newPluginState === void 0 ? void 0 : _newPluginState.isInverted,
diffType: (_newPluginState2 = newPluginState) === null || _newPluginState2 === void 0 ? void 0 : _newPluginState2.diffType
} : {}));
// Update the decorations
newPluginState.decorations = decorations;
} else if ((meta === null || meta === void 0 ? void 0 : meta.action) === 'HIDE_DIFF') {
newPluginState = _objectSpread(_objectSpread(_objectSpread({}, currentPluginState), meta), {}, {
decorations: DecorationSet.empty,
isDisplayingChanges: false,
activeIndex: undefined
}, expValEquals('platform_editor_diff_plugin_extended', 'isEnabled', true) ? {
isInverted: false,
diffType: 'inline'
} : {});
} else if (((meta === null || meta === void 0 ? void 0 : meta.action) === 'SCROLL_TO_NEXT' || (meta === null || meta === void 0 ? void 0 : meta.action) === 'SCROLL_TO_PREVIOUS') && fg('platform_editor_show_diff_scroll_navigation')) {
// Update the active index in plugin state and recalculate decorations
var _decorations = getScrollableDecorations(currentPluginState.decorations, newState.doc);
if (_decorations.length > 0) {
var _currentPluginState$a;
// Initialize to -1 if undefined so that the first "next" scroll takes us to index 0 (first change).
// This allows the UI to start with no selection and only highlight on first user interaction.
var nextIndex = (_currentPluginState$a = currentPluginState.activeIndex) !== null && _currentPluginState$a !== void 0 ? _currentPluginState$a : -1;
if (meta.action === 'SCROLL_TO_NEXT') {
nextIndex = (nextIndex + 1) % _decorations.length;
} else {
// Handle scrolling backwards from the uninitialized state.
// If at -1 (no selection), wrap to the last decoration.
if (nextIndex === -1) {
nextIndex = _decorations.length - 1;
} else {
nextIndex = (nextIndex - 1 + _decorations.length) % _decorations.length;
}
}
var activeDecoration = _decorations[nextIndex];
newPluginState = _objectSpread(_objectSpread({}, currentPluginState), {}, {
activeIndex: nextIndex,
activeIndexPos: activeDecoration ? {
from: activeDecoration.from,
to: activeDecoration.to
} : undefined
});
// Recalculate decorations with the new active index
var updatedDecorations = calculateDiffDecorations(_objectSpread({
state: newState,
pluginState: newPluginState,
nodeViewSerializer: nodeViewSerializer,
colorScheme: config === null || config === void 0 ? void 0 : config.colorScheme,
intl: getIntl(),
activeIndexPos: newPluginState.activeIndexPos,
api: api
}, expValEquals('platform_editor_diff_plugin_extended', 'isEnabled', true) ? {
isInverted: newPluginState.isInverted
} : {}));
newPluginState.decorations = updatedDecorations;
}
} else {
newPluginState = _objectSpread(_objectSpread({}, currentPluginState), meta);
}
}
return _objectSpread(_objectSpread({}, newPluginState), {}, {
decorations: newPluginState.decorations.map(tr.mapping, tr.doc)
});
}
},
view: function view(editorView) {
setNodeViewSerializer(editorView);
var isFirst = true;
var previousActiveIndex;
var cancelPendingScrollToDecoration = null;
return {
update: function update(view) {
// If we're using configuration to show diffs we initialise here once we setup the editor view
if (config !== null && config !== void 0 && config.originalDoc && config !== null && config !== void 0 && config.steps && config.steps.length > 0 && isFirst) {
isFirst = false;
view.dispatch(view.state.tr.setMeta(showDiffPluginKey, {
action: 'SHOW_DIFF',
steps: config.steps.map(function (step) {
return ProseMirrorStep.fromJSON(view.state.schema, step);
}),
originalDoc: processRawValue(view.state.schema, config.originalDoc)
}));
}
// Check for any potential scroll side-effects
if (fg('platform_editor_show_diff_scroll_navigation')) {
var pluginState = showDiffPluginKey.getState(view.state);
var activeIndexChanged = (pluginState === null || pluginState === void 0 ? void 0 : pluginState.activeIndex) !== undefined && pluginState.activeIndex !== previousActiveIndex;
previousActiveIndex = pluginState === null || pluginState === void 0 ? void 0 : pluginState.activeIndex;
if ((pluginState === null || pluginState === void 0 ? void 0 : pluginState.activeIndex) !== undefined && activeIndexChanged) {
var _cancelPendingScrollT;
(_cancelPendingScrollT = cancelPendingScrollToDecoration) === null || _cancelPendingScrollT === void 0 || _cancelPendingScrollT();
cancelPendingScrollToDecoration = scrollToActiveDecoration(view, getScrollableDecorations(pluginState.decorations, view.state.doc), pluginState.activeIndex);
}
}
},
destroy: function destroy() {
var _cancelPendingScrollT2;
(_cancelPendingScrollT2 = cancelPendingScrollToDecoration) === null || _cancelPendingScrollT2 === void 0 || _cancelPendingScrollT2();
cancelPendingScrollToDecoration = null;
}
};
},
props: {
decorations: function decorations(state) {
var pluginState = showDiffPluginKey.getState(state);
return pluginState === null || pluginState === void 0 ? void 0 : pluginState.decorations;
}
}
});
};