UNPKG

@atlaskit/editor-plugin-show-diff

Version:

ShowDiff plugin for @atlaskit/editor-core

184 lines (183 loc) 9.2 kB
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 const showDiffPluginKey = new PluginKey('showDiffPlugin'); export const createPlugin = (config, getIntl, api) => { if (fg('platform_editor_show_diff_equality_fallback')) { enforceCustomStepRegisters(); } const nodeViewSerializer = new NodeViewSerializer({}); const setNodeViewSerializer = editorView => { nodeViewSerializer.init({ editorView }); }; return new SafePlugin({ key: showDiffPluginKey, state: { init(_, _state) { // We do initial setup after we setup the editor view return { steps: [], originalDoc: undefined, decorations: DecorationSet.empty, isDisplayingChanges: false, ...(expValEquals('platform_editor_diff_plugin_extended', 'isEnabled', true) ? { isInverted: false, diffType: 'inline' } : {}) }; }, apply: (tr, currentPluginState, oldState, newState) => { const meta = tr.getMeta(showDiffPluginKey); let 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 = { ...currentPluginState, ...meta, isDisplayingChanges: true, activeIndex: undefined }; // Calculate and store decorations in state const decorations = calculateDiffDecorations({ state: newState, pluginState: newPluginState, 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, ...(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 = { ...currentPluginState, ...meta, decorations: DecorationSet.empty, isDisplayingChanges: false, activeIndex: undefined, /** * Reset isInverted & diffType state when hiding diffs * Otherwise this should persist for the diff-showing session */ ...(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 const 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. let 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; } } const activeDecoration = decorations[nextIndex]; newPluginState = { ...currentPluginState, activeIndex: nextIndex, activeIndexPos: activeDecoration ? { from: activeDecoration.from, to: activeDecoration.to } : undefined }; // Recalculate decorations with the new active index const updatedDecorations = calculateDiffDecorations({ state: newState, pluginState: newPluginState, nodeViewSerializer, colorScheme: config === null || config === void 0 ? void 0 : config.colorScheme, intl: getIntl(), activeIndexPos: newPluginState.activeIndexPos, api, ...(expValEquals('platform_editor_diff_plugin_extended', 'isEnabled', true) ? { isInverted: newPluginState.isInverted } : {}) }); newPluginState.decorations = updatedDecorations; } } else { newPluginState = { ...currentPluginState, ...meta }; } } return { ...newPluginState, decorations: newPluginState.decorations.map(tr.mapping, tr.doc) }; } }, view(editorView) { setNodeViewSerializer(editorView); let isFirst = true; let previousActiveIndex; let cancelPendingScrollToDecoration = null; return { 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(step => 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')) { const pluginState = showDiffPluginKey.getState(view.state); const 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 ? void 0 : _cancelPendingScrollT(); cancelPendingScrollToDecoration = scrollToActiveDecoration(view, getScrollableDecorations(pluginState.decorations, view.state.doc), pluginState.activeIndex); } } }, destroy() { var _cancelPendingScrollT2; (_cancelPendingScrollT2 = cancelPendingScrollToDecoration) === null || _cancelPendingScrollT2 === void 0 ? void 0 : _cancelPendingScrollT2(); cancelPendingScrollToDecoration = null; } }; }, props: { decorations: state => { const pluginState = showDiffPluginKey.getState(state); return pluginState === null || pluginState === void 0 ? void 0 : pluginState.decorations; } } }); };