@atlaskit/editor-plugin-show-diff
Version:
ShowDiff plugin for @atlaskit/editor-core
111 lines (106 loc) • 4.99 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.scrollToFirstDecoration = exports.scrollToActiveDecoration = void 0;
/**
* Extra space above the scrolled-to element so it does not sit flush under the
* viewport edge (helps with sticky table headers, toolbars, etc.).
*
* Implemented with `scroll-margin-top` so we still use the browser’s native
* `scrollIntoView`, which scrolls every relevant scrollport (nested containers
* and the window). A single manual `scrollTop` on one ancestor often misses
* outer scroll or mis-identifies the active scroll container.
*/
var SCROLL_TOP_MARGIN_PX = 100;
/**
* Returns the resolved HTMLElement for a given DOM node, walking up to the
* parent element if the node itself is not an Element (e.g. a text node).
*/
function scrollToSelection(node) {
var element = node instanceof Element ? node : (node === null || node === void 0 ? void 0 : node.parentElement) instanceof Element ? node.parentElement : null;
if (!(element instanceof HTMLElement)) {
return;
}
// scroll-margin is included in scroll-into-view math; it does not change layout.
var previousScrollMarginTop = element.style.scrollMarginTop;
element.style.scrollMarginTop = "".concat(SCROLL_TOP_MARGIN_PX, "px");
try {
element.scrollIntoView({
behavior: 'smooth',
block: 'start'
});
} finally {
element.style.scrollMarginTop = previousScrollMarginTop;
}
}
/**
* Schedules scrolling to the first diff decoration after the next frame.
* Unlike `scrollToActiveDecoration`, this does not require an active index —
* it simply scrolls to bring the first decoration into view.
*
* @returns A function that cancels the scheduled `requestAnimationFrame` if it has not run yet.
*/
var scrollToFirstDecoration = exports.scrollToFirstDecoration = function scrollToFirstDecoration(view, decorations) {
var decoration = decorations[0];
if (!decoration) {
return function () {};
}
var rafId = requestAnimationFrame(function () {
var _decoration$spec, _decoration$type;
rafId = null;
// @ts-expect-error - decoration.type is not typed public API
if ((_decoration$spec = decoration.spec) !== null && _decoration$spec !== void 0 && (_decoration$spec = _decoration$spec.key) !== null && _decoration$spec !== void 0 && _decoration$spec.startsWith('diff-widget') && decoration !== null && decoration !== void 0 && (_decoration$type = decoration.type) !== null && _decoration$type !== void 0 && _decoration$type.toDOM) {
// @ts-expect-error - decoration.type is not typed public API
var widgetDom = decoration.type.toDOM;
// Always scroll to the top of this decoration even if it's in view already
scrollToSelection(widgetDom);
} else {
var _view$domAtPos;
var targetNode = view.nodeDOM(decoration === null || decoration === void 0 ? void 0 : decoration.from);
var node = targetNode instanceof Element ? targetNode : (_view$domAtPos = view.domAtPos(decoration === null || decoration === void 0 ? void 0 : decoration.from)) === null || _view$domAtPos === void 0 ? void 0 : _view$domAtPos.node;
if (node instanceof HTMLElement) {
// Always scroll to the top of this decoration even if it's in view already
scrollToSelection(node);
}
}
});
return function () {
if (rafId !== null) {
cancelAnimationFrame(rafId);
rafId = null;
}
};
};
/**
* Schedules scrolling to the decoration at the given index after the next frame.
*
* @returns A function that cancels the scheduled `requestAnimationFrame` if it has not run yet.
*/
var scrollToActiveDecoration = exports.scrollToActiveDecoration = function scrollToActiveDecoration(view, decorations, activeIndex) {
var decoration = decorations[activeIndex];
if (!decoration) {
return function () {};
}
var rafId = requestAnimationFrame(function () {
var _decoration$spec2;
rafId = null;
if (((_decoration$spec2 = decoration.spec) === null || _decoration$spec2 === void 0 ? void 0 : _decoration$spec2.key) === 'diff-widget-active') {
var _decoration$type2;
// @ts-expect-error - decoration.type is not typed public API
var widgetDom = decoration === null || decoration === void 0 || (_decoration$type2 = decoration.type) === null || _decoration$type2 === void 0 ? void 0 : _decoration$type2.toDOM;
scrollToSelection(widgetDom);
} else {
var _view$domAtPos2;
var targetNode = view.nodeDOM(decoration === null || decoration === void 0 ? void 0 : decoration.from);
var node = targetNode instanceof Element ? targetNode : (_view$domAtPos2 = view.domAtPos(decoration === null || decoration === void 0 ? void 0 : decoration.from)) === null || _view$domAtPos2 === void 0 ? void 0 : _view$domAtPos2.node;
scrollToSelection(node);
}
});
return function () {
if (rafId !== null) {
cancelAnimationFrame(rafId);
rafId = null;
}
};
};