@atlaskit/editor-plugin-show-diff
Version:
ShowDiff plugin for @atlaskit/editor-core
105 lines (100 loc) • 5.14 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.scrollToDecoration = void 0;
var _styles = require("@atlaskit/editor-common/styles");
var _getStickyHeaderHeight = require("@atlaskit/editor-common/table/get-sticky-header-height");
var _fg = require("@atlaskit/platform-feature-flags/fg");
var _decorationKeys = require("./decorations/decorationKeys");
/**
* 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;
function updateScrollTargetMargin(view, targetPos, api) {
var _api$limitedMode;
var isLimitedModeEnabled = Boolean(api === null || api === void 0 || (_api$limitedMode = api.limitedMode) === null || _api$limitedMode === void 0 || (_api$limitedMode = _api$limitedMode.sharedState.currentState()) === null || _api$limitedMode === void 0 ? void 0 : _api$limitedMode.enabled);
var tableHeaderHeight = isLimitedModeEnabled ? undefined : (0, _getStickyHeaderHeight.getStickyHeaderHeight)(view, targetPos);
view.dom.style.setProperty(_decorationKeys.SCROLL_TARGET_MARGIN_CSS_PROPERTY, "".concat(tableHeaderHeight ? tableHeaderHeight + _styles.tableMarginTop : 0, "px"));
}
/**
* 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;
}
if ((0, _fg.fg)('platform_editor_ai_show_diff_patch_1')) {
element.scrollIntoView({
behavior: 'smooth',
block: 'start'
});
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 decoration at the given index after the next frame. Defaults to the
* first decoration when no index is provided.
*
* @returns A function that cancels the scheduled `requestAnimationFrame` if it has not run yet.
*/
var scrollToDecoration = exports.scrollToDecoration = function scrollToDecoration(view, decorations) {
var activeIndex = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 0;
var api = arguments.length > 3 ? arguments[3] : undefined;
var decoration = decorations[activeIndex];
if (!decoration) {
return function () {};
}
// A grouped decoration spans a whole customer-facing edit, which can visually start with a
// deleted-content widget rather than at the group's `from` position — see `scrollTarget` in
// `getScrollableDecorations`. Only set under `platform_editor_ai_show_diff_patch_1`.
var target = (0, _decorationKeys.isDiffDecoration)(decoration) && decoration.spec.scrollTarget || decoration;
var rafId = requestAnimationFrame(function () {
rafId = null;
if ((0, _fg.fg)('platform_editor_ai_show_diff_patch_1')) {
updateScrollTargetMargin(view, target.from, api);
}
if ((0, _decorationKeys.isDiffDecoration)(target) && target.spec.decorationType === 'widget') {
var _target$type;
// @ts-expect-error - decoration.type is not typed public API
var widgetDom = target === null || target === void 0 || (_target$type = target.type) === null || _target$type === void 0 ? void 0 : _target$type.toDOM;
scrollToSelection(widgetDom);
} else if ((0, _decorationKeys.isDiffDecoration)(target) && (0, _fg.fg)('platform_editor_ai_show_diff_patch_2')) {
var _Array$from$find;
var targetNode = (_Array$from$find = Array.from(view.dom.querySelectorAll('[data-diff-id]')).find(function (element) {
return element.dataset.diffId === target.spec.diffId;
})) !== null && _Array$from$find !== void 0 ? _Array$from$find : view.nodeDOM(target === null || target === void 0 ? void 0 : target.from);
scrollToSelection(targetNode);
} else {
var _view$domAtPos;
var _targetNode = view.nodeDOM(target === null || target === void 0 ? void 0 : target.from);
var node = _targetNode instanceof Element ? _targetNode : (_view$domAtPos = view.domAtPos(target === null || target === void 0 ? void 0 : target.from)) === null || _view$domAtPos === void 0 ? void 0 : _view$domAtPos.node;
scrollToSelection(node);
}
});
return function () {
if (rafId !== null) {
cancelAnimationFrame(rafId);
rafId = null;
}
};
};