UNPKG

@atlaskit/editor-plugin-find-replace

Version:

find replace plugin for @atlaskit/editor-core

107 lines (103 loc) 4.58 kB
import { pluginFactory, stepHasSlice } from '@atlaskit/editor-common/utils'; import { DecorationSet } from '@atlaskit/editor-prosemirror/view'; import { expValEquals } from '@atlaskit/tmp-editor-statsig/exp-val-equals'; import { initialState } from './main'; import { findReplacePluginKey } from './plugin-key'; import reducer from './reducer'; import { createDecorations, findClosestMatch, findDecorationFromMatch, findMatches, findSearchIndex, findUniqueItemsIn, getSelectionForMatch, isMatchAffectedByStep, removeDecorationsFromSet, removeMatchesFromSet } from './utils'; const handleDocChanged = (tr, pluginState) => { var _api$expand; const { isActive, findText } = pluginState; if (!isActive || !findText) { return pluginState; } if (!tr.steps.find(stepHasSlice)) { return pluginState; } // Ignored via go/ees005 // eslint-disable-next-line prefer-const let { index, decorationSet, matches, shouldMatchCase, getIntl, api } = pluginState; const newMatches = findMatches({ content: tr.doc, searchText: findText, shouldMatchCase: shouldMatchCase, getIntl, api }); decorationSet = decorationSet.map(tr.mapping, tr.doc); const numDecorations = decorationSet.find().length; const mappedMatches = matches.map(match => ({ start: tr.mapping.map(match.start), end: tr.mapping.map(match.end), canReplace: match.canReplace, nodeType: match.nodeType })); let matchesToAdd = []; let matchesToDelete = []; if (newMatches.length > 0 && numDecorations === 0) { matchesToAdd = newMatches; } else if (newMatches.length === 0 && numDecorations > 0) { decorationSet = DecorationSet.empty; } else if (newMatches.length > 0 || numDecorations > 0) { // go through tr steps and find any new matches from user adding content or // any dead matches from user deleting content tr.steps.forEach(step => { if (stepHasSlice(step)) { // add all matches that are between the affected positions and don't already have // corresponding decorations matchesToAdd = [...matchesToAdd, ...newMatches.filter(match => isMatchAffectedByStep(match, step, tr) && !findDecorationFromMatch(decorationSet, match))]; // delete any matches that are missing from the newMatches array and have a // corresponding decoration matchesToDelete = [...matchesToDelete, ...findUniqueItemsIn(mappedMatches.filter(match => isMatchAffectedByStep(match, step, tr) && !!findDecorationFromMatch(decorationSet, match)), newMatches, (firstMatch, secondMatch) => firstMatch.start === secondMatch.start && firstMatch.end === secondMatch.end)]; } }); } // update decorations if matches changed following document update if (matchesToDelete.length > 0) { const decorationsToDelete = matchesToDelete.reduce((decorations, match) => [...decorations, ...decorationSet.find(match.start, match.end)], []); decorationSet = removeDecorationsFromSet(decorationSet, decorationsToDelete, tr.doc); } if (matchesToAdd.length > 0) { decorationSet = decorationSet.add(tr.doc, createDecorations(tr.selection.from, matchesToAdd)); } // update selected match if it has changed let newIndex = index; const selectedMatch = mappedMatches[index]; if (selectedMatch) { newIndex = newMatches.findIndex(match => match.start === selectedMatch.start); } if (newIndex === undefined || newIndex === -1) { newIndex = expValEquals('platform_editor_find_and_replace_improvements', 'isEnabled', true) ? findClosestMatch(tr.selection.from, newMatches) : findSearchIndex(tr.selection.from, newMatches); } const newSelectedMatch = newMatches[newIndex]; decorationSet = removeMatchesFromSet(decorationSet, [selectedMatch, newSelectedMatch], tr.doc); if (newSelectedMatch) { decorationSet = decorationSet.add(tr.doc, createDecorations(0, [newSelectedMatch])); } const newSelection = getSelectionForMatch(tr.selection, tr.doc, newIndex, newMatches); api === null || api === void 0 ? void 0 : (_api$expand = api.expand) === null || _api$expand === void 0 ? void 0 : _api$expand.commands.toggleExpandWithMatch(newSelection)({ tr: tr }); return { ...pluginState, matches: newMatches, index: newIndex, decorationSet }; }; const dest = pluginFactory(findReplacePluginKey, reducer(() => initialState), { onDocChanged: handleDocChanged }); export const createCommand = dest.createCommand; export const getPluginState = dest.getPluginState; export const createPluginState = dest.createPluginState;