UNPKG

@atlaskit/editor-plugin-find-replace

Version:

find replace plugin for @atlaskit/editor-core

314 lines (311 loc) 13.2 kB
"use strict"; var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault"); Object.defineProperty(exports, "__esModule", { value: true }); exports.default = void 0; var _regenerator = _interopRequireDefault(require("@babel/runtime/regenerator")); var _asyncToGenerator2 = _interopRequireDefault(require("@babel/runtime/helpers/asyncToGenerator")); var _classCallCheck2 = _interopRequireDefault(require("@babel/runtime/helpers/classCallCheck")); var _createClass2 = _interopRequireDefault(require("@babel/runtime/helpers/createClass")); var _expValEquals = require("@atlaskit/tmp-editor-statsig/exp-val-equals"); var _pluginFactory = require("../plugin-factory"); var _index = require("./index"); // max number of decorations to apply at once var batchIncrement = 100; // position range to apply decorations between before alternating above or below viewport var posIncrement = 2000; /** * Provides support for applying search match highlight decorations in batches */ var BatchDecorations = /*#__PURE__*/function () { function BatchDecorations() { (0, _classCallCheck2.default)(this, BatchDecorations); } return (0, _createClass2.default)(BatchDecorations, [{ key: "stop", value: function stop() { if (this.rafId) { cancelAnimationFrame(this.rafId); } } /** * Applies the decorations needed for the current search results * It does so async, splitting them up into batches to help with performance */ }, { key: "applyAllSearchDecorations", value: (function () { var _applyAllSearchDecorations = (0, _asyncToGenerator2.default)( /*#__PURE__*/_regenerator.default.mark(function _callee(editorView, containerElement, addDecorations, removeDecorations) { var pmElement, positions, startPos, endPos, viewportStartPos, viewportEndPos, dir, before, after, diff; return _regenerator.default.wrap(function _callee$(_context) { while (1) switch (_context.prev = _context.next) { case 0: this.stop(); this.addDecorations = addDecorations; this.removeDecorations = removeDecorations; if (containerElement) { _context.next = 5; break; } return _context.abrupt("return"); case 5: pmElement = containerElement.querySelector('.ProseMirror'); if (pmElement) { _context.next = 8; break; } return _context.abrupt("return"); case 8: positions = this.calcDecorationPositions(editorView, containerElement, pmElement); startPos = positions.startPos, endPos = positions.endPos, viewportStartPos = positions.viewportStartPos, viewportEndPos = positions.viewportEndPos; dir = 0; before = viewportStartPos; after = viewportEndPos - posIncrement; _context.next = 15; return this.updateDecorationsBetween(editorView, viewportStartPos, viewportEndPos); case 15: if (!(before > startPos || after < endPos)) { _context.next = 28; break; } if (!(dir++ % 2 === 0 && before > startPos || after >= endPos)) { _context.next = 23; break; } diff = before - startPos; before = Math.max(before - posIncrement, startPos); _context.next = 21; return this.updateDecorationsBetween(editorView, before, before + Math.min(diff, posIncrement)); case 21: _context.next = 26; break; case 23: after = Math.min(after + posIncrement, endPos); _context.next = 26; return this.updateDecorationsBetween(editorView, after, Math.min(after + posIncrement, endPos)); case 26: _context.next = 15; break; case 28: case "end": return _context.stop(); } }, _callee, this); })); function applyAllSearchDecorations(_x, _x2, _x3, _x4) { return _applyAllSearchDecorations.apply(this, arguments); } return applyAllSearchDecorations; }()) }, { key: "updateDecorationsBetween", value: function () { var _updateDecorationsBetween = (0, _asyncToGenerator2.default)( /*#__PURE__*/_regenerator.default.mark(function _callee2(editorView, startPos, endPos) { return _regenerator.default.wrap(function _callee2$(_context2) { while (1) switch (_context2.prev = _context2.next) { case 0: _context2.next = 2; return this.removeDecorationsBetween(editorView, startPos, endPos); case 2: _context2.next = 4; return this.addDecorationsBetween(editorView, startPos, endPos); case 4: case "end": return _context2.stop(); } }, _callee2, this); })); function updateDecorationsBetween(_x5, _x6, _x7) { return _updateDecorationsBetween.apply(this, arguments); } return updateDecorationsBetween; }() }, { key: "addDecorationsBetween", value: function () { var _addDecorationsBetween = (0, _asyncToGenerator2.default)( /*#__PURE__*/_regenerator.default.mark(function _callee3(editorView, startPos, endPos) { var _this = this; var selection, _getPluginState, matches, decorationSet, matchesBetween, selectionMatch, selectionIndex; return _regenerator.default.wrap(function _callee3$(_context3) { while (1) switch (_context3.prev = _context3.next) { case 0: selection = editorView.state.selection; _getPluginState = (0, _pluginFactory.getPluginState)(editorView.state), matches = _getPluginState.matches, decorationSet = _getPluginState.decorationSet; if (!(matches.length === 0)) { _context3.next = 4; break; } return _context3.abrupt("return"); case 4: matchesBetween = matches.filter(function (m) { return m.start >= startPos && (endPos === undefined || m.start < endPos); }); selectionMatch = matches.find(function (match) { return match.start >= selection.from; }); selectionIndex = matchesBetween.findIndex(function (match) { return match === selectionMatch; }); _context3.next = 9; return this.batchRequests(function (counter) { var matchesToDecorate = matchesBetween.slice(counter, counter + batchIncrement); if (matchesToDecorate.length === 0) { return false; } var useSelectionIndex = selectionIndex >= counter && selectionIndex < counter + batchIncrement; if (selectionMatch && useSelectionIndex) { var selectionMatchDecoration = (0, _index.findDecorationFromMatch)(decorationSet, selectionMatch); if (selectionMatchDecoration) { matchesToDecorate.splice(selectionIndex % batchIncrement, 1); useSelectionIndex = false; } } if (_this.addDecorations) { _this.addDecorations((0, _index.createDecorations)(useSelectionIndex ? selectionIndex % batchIncrement : -1, matchesToDecorate)); } }, { increment: batchIncrement, until: matchesBetween.length }); case 9: return _context3.abrupt("return", _context3.sent); case 10: case "end": return _context3.stop(); } }, _callee3, this); })); function addDecorationsBetween(_x8, _x9, _x0) { return _addDecorationsBetween.apply(this, arguments); } return addDecorationsBetween; }() }, { key: "removeDecorationsBetween", value: function () { var _removeDecorationsBetween = (0, _asyncToGenerator2.default)( /*#__PURE__*/_regenerator.default.mark(function _callee4(editorView, startPos, endPos) { var _this2 = this; var _getPluginState2, decorationSet, decorations; return _regenerator.default.wrap(function _callee4$(_context4) { while (1) switch (_context4.prev = _context4.next) { case 0: _getPluginState2 = (0, _pluginFactory.getPluginState)(editorView.state), decorationSet = _getPluginState2.decorationSet; decorations = decorationSet.find(startPos, endPos); if (!(decorations.length === 0)) { _context4.next = 4; break; } return _context4.abrupt("return"); case 4: _context4.next = 6; return this.batchRequests(function (counter) { var decorationsToRemove = decorations.slice(counter, counter + batchIncrement); if (decorationsToRemove.length === 0) { return false; } // only get those decorations whose from >= startPos for (var i = 0; i < decorationsToRemove.length; i++) { if (decorationsToRemove[i].from >= startPos) { break; } decorationsToRemove = decorationsToRemove.slice(1); } if (_this2.removeDecorations) { _this2.removeDecorations(decorationsToRemove); } }, { increment: batchIncrement, until: decorations.length }); case 6: return _context4.abrupt("return", _context4.sent); case 7: case "end": return _context4.stop(); } }, _callee4, this); })); function removeDecorationsBetween(_x1, _x10, _x11) { return _removeDecorationsBetween.apply(this, arguments); } return removeDecorationsBetween; }() /** * Calculates Prosemirror start and end positions we want to apply the decorations * between * Also calculates the positions the are the start and end of the user's viewport * so we can apply decorations there first and work outwards */ }, { key: "calcDecorationPositions", value: function calcDecorationPositions(editorView, containerElement, pmElement) { var containerRect = containerElement.getBoundingClientRect(); var pmRect = pmElement.getBoundingClientRect(); var viewportStartPos = this.getStartPos(editorView, 0, pmRect.left); var viewportEndPos = this.getEndPos(editorView, containerRect.top + containerRect.height, pmRect.left); return { viewportStartPos: viewportStartPos, viewportEndPos: viewportEndPos, startPos: (0, _expValEquals.expValEquals)('platform_editor_find_and_replace_improvements', 'isEnabled', true) ? 0 : 1, endPos: editorView.state.doc.nodeSize }; } }, { key: "getStartPos", value: function getStartPos(editorView, y, x) { var startPos = editorView.posAtCoords({ top: y, left: x }); if ((0, _expValEquals.expValEquals)('platform_editor_find_and_replace_improvements', 'isEnabled', true)) { return startPos ? startPos.pos : 0; } else { return startPos ? startPos.pos : 1; } } }, { key: "getEndPos", value: function getEndPos(editorView, y, x) { var maxPos = editorView.state.doc.nodeSize; var endPos = editorView.posAtCoords({ top: y, left: x }); return endPos ? endPos.pos : maxPos; } /** * Util to batch function calls by animation frames * A counter will start at 0 and increment by provided value until reaches limit * Passed in fn receives the counter as a param, return false to skip waiting * for the animation frame for the next call */ }, { key: "batchRequests", value: function batchRequests(fn, opts) { var _this3 = this; var counter = 0; var increment = opts.increment, until = opts.until; return new Promise(function (resolve) { var _batchedFn = function batchedFn() { var result = fn(counter); while (result === false && counter < until) { counter += increment; result = fn(counter); } if (counter < until) { counter += increment; _this3.rafId = requestAnimationFrame(_batchedFn); } else { _this3.rafId = undefined; resolve(); } }; _this3.rafId = requestAnimationFrame(_batchedFn); }); } }]); }(); var batchDecorations = new BatchDecorations(); var _default = exports.default = batchDecorations;