@atlaskit/editor-plugin-show-diff
Version:
ShowDiff plugin for @atlaskit/editor-core
617 lines (592 loc) • 28.9 kB
JavaScript
;
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.createLeftAnchorWidget = exports.createInlineIndicatorAnchorWidgets = exports.createDocMarginAnchorWidget = exports.createBlockIndicatorAnchorWidgets = exports.createAnchorNameSpan = exports.clampAnchorPosIntoCell = void 0;
var _toConsumableArray2 = _interopRequireDefault(require("@babel/runtime/helpers/toConsumableArray"));
var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty"));
var _utils = require("@atlaskit/editor-prosemirror/utils");
var _view = require("@atlaskit/editor-prosemirror/view");
var _fg = require("@atlaskit/platform-feature-flags/fg");
var _decorationKeys = require("./decorationKeys");
function _createForOfIteratorHelper(r, e) { var t = "undefined" != typeof Symbol && r[Symbol.iterator] || r["@@iterator"]; if (!t) { if (Array.isArray(r) || (t = _unsupportedIterableToArray(r)) || e && r && "number" == typeof r.length) { t && (r = t); var _n = 0, F = function F() {}; return { s: F, n: function n() { return _n >= r.length ? { done: !0 } : { done: !1, value: r[_n++] }; }, e: function e(r) { throw r; }, f: F }; } throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } var o, a = !0, u = !1; return { s: function s() { t = t.call(r); }, n: function n() { var r = t.next(); return a = r.done, r; }, e: function e(r) { u = !0, o = r; }, f: function f() { try { a || null == t.return || t.return(); } finally { if (u) throw o; } } }; }
function _unsupportedIterableToArray(r, a) { if (r) { if ("string" == typeof r) return _arrayLikeToArray(r, a); var t = {}.toString.call(r).slice(8, -1); return "Object" === t && r.constructor && (t = r.constructor.name), "Map" === t || "Set" === t ? Array.from(r) : "Arguments" === t || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(t) ? _arrayLikeToArray(r, a) : void 0; } }
function _arrayLikeToArray(r, a) { (null == a || a > r.length) && (a = r.length); for (var e = 0, n = Array(a); e < a; e++) n[e] = r[e]; return n; }
function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { (0, _defineProperty2.default)(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
/**
* Resolves the doc-level block node (table/expand/layout) for `from`, along with
* the position right before it (`beforePos`). Falls back to `$from.nodeAfter`
* when there is no depth-1 ancestor (e.g. `from` sits just before the block).
*/
var resolveDocLevelNode = function resolveDocLevelNode(doc, from) {
var _$from$node;
var $from = doc.resolve(from);
var node = (_$from$node = $from.node(1)) !== null && _$from$node !== void 0 ? _$from$node : $from.nodeAfter;
if (!node) {
return undefined;
}
// Content start of the block. For the depth-1 case this is `$from.start(1)`;
// for the `nodeAfter` fallback, `from` is the position just before the block
// node, so its content starts at `from + 1`.
var nodeStart = $from.node(1) ? $from.start(1) : from + 1;
return {
node: node,
nodeStart: nodeStart,
// Position of the block node itself (one before its content start).
beforePos: nodeStart - 1
};
};
/**
* Handles edge cases for block nodes whose inline content can exceed the doc
* margin (tables, layouts, expands). Returns the position whose DOM should be
* measured to size the left anchor, or `undefined` when the diff is not inside
* such a node.
*/
var edgeCases = function edgeCases(doc, from, nodeOverride) {
var resolved = resolveDocLevelNode(doc, from);
if (!resolved) {
return undefined;
}
var resolvedNode = resolved.node,
nodeStart = resolved.nodeStart,
beforePos = resolved.beforePos;
var node = (0, _fg.fg)('platform_editor_ai_show_diff_patch_2') ? nodeOverride !== null && nodeOverride !== void 0 ? nodeOverride : resolvedNode : resolvedNode;
if (node.type.name === 'layoutSection' && (0, _fg.fg)('platform_editor_ai_show_diff_patch_1')) {
// Columns extend past the node-view wrapper via negative margins (12px or 20px).
// Measure their container so the indicator stays outside the diff outline, including
// breakout layouts and after responsive resizing, without changing the column geometry.
return {
beforePos: beforePos,
measurePos: beforePos,
measureSelector: '[data-layout-section]'
};
}
/**
* All resizable nodes will need dynamic calculations of the block indicator left anchor
*/
if (node.marks.some(function (mark) {
return mark.type.name === 'breakout';
})) {
/**
* Layouts and Expands have extra padding around the container
*/
return {
beforePos: beforePos,
measurePos: beforePos
};
}
switch (node.type.name) {
/**
* For resizable blocks, inline content can exceed the doc margin.
* The widget is placed before the block; the anchor is sized against the
* block's DOM so it doesn't get clipped when the block is resized :')
*/
case 'table':
{
// A table with no rows has nothing to measure.
if (!node.firstChild) {
return undefined;
}
if ((0, _fg.fg)('platform_editor_ai_show_diff_patch_2')) {
// The row retains its full width when the table scrolls. Centering that
// width against the editor would push the anchor off-screen. Measure the
// visible container's edge instead, including start-aligned tables.
return {
beforePos: beforePos,
measurePos: beforePos,
measureSelector: '.pm-table-container',
measureLeft: true
};
}
// Measure the first row (`nodeStart` is just inside the table, i.e. the
// position of the first row): its width matches the table's.
return {
beforePos: beforePos,
measurePos: nodeStart
};
}
case 'layoutSection':
case 'expand':
// Measure the block itself (the widget is rendered outside the block).
return {
beforePos: beforePos,
leftOffset: 12
};
default:
return undefined;
}
};
var getEdgeCasesForSlice = function getEdgeCasesForSlice(doc, from, slice, measureElement) {
var _targets$find;
var resolved = resolveDocLevelNode(doc, from);
if (!resolved) {
return [];
}
var targets = [];
// edge cases can only be top level nodes, so we only care about the top level nodes in the slice
slice.content.forEach(function (node, offset) {
// the node can be outside the doc if we have a sliceOverride which adds additional new content
// but we still need to the edgeCases to resolve a node in the doc to attach the widget to
// and we use the provided measureElement to make sure it is in the right position
var edgeCase = edgeCases(doc, Math.min(from + offset, doc.content.size - 1), node);
if (edgeCase) {
targets.push(_objectSpread(_objectSpread({}, edgeCase), {}, {
measureElement: measureElement
}));
}
});
return targets.length > 0 ? [{
beforePos: resolved.beforePos,
leftOffset: (_targets$find = targets.find(function (target) {
return target.leftOffset !== undefined;
})) === null || _targets$find === void 0 ? void 0 : _targets$find.leftOffset,
widthMeasureTargets: targets
}] : [];
};
/**
* Finds the edge cases in the current document for either a whole range or one position.
* A missing `to` intentionally means the single-position lookup is the fallback.
*/
var getEdgeCasesForDocument = function getEdgeCasesForDocument(doc, from, to) {
if (to !== undefined) {
var edgeCasesForSlice = getEdgeCasesForSlice(doc, from, doc.slice(from, to));
if (edgeCasesForSlice.length > 0) {
return edgeCasesForSlice;
}
}
var edgeCase = edgeCases(doc, from);
return edgeCase ? [edgeCase] : [];
};
/**
* Create a widget that marks the start of the doc margin.
* This is used to determine the position of the inline indicators
* when the inline content exceeds the doc margin.
*/
var createDocMarginAnchorWidget = exports.createDocMarginAnchorWidget = function createDocMarginAnchorWidget() {
return _view.Decoration.widget(0, function () {
var span = document.createElement('span');
span.style.setProperty('anchor-name', "--".concat(_decorationKeys.AnchorDocMarginKey));
return span;
},
// set the side to -999 so that it is always rendered before any other anchors
(0, _decorationKeys.buildAnchorDecorationSpec)({
anchorType: _decorationKeys.AnchorTypeKey.docMargin,
side: -999
}));
};
/**
* Creates an invisible left anchor widget for a diff inside a resizable block
* node (table, layout, expand), whose inline content can exceed the doc margin.
* Resolves the edge-case position from `doc`/`from`; returns `undefined` when
* the diff is not inside such a node and no left anchor is needed.
*
* The span is given an `anchor-name` (keyed by `diffId`) and positioned in the
* doc margin so the `IndicatorBar` can align its left edge against it via CSS
* anchor positioning. Shared by inline and node (widget) diff decorations.
*/
var createLeftAnchorWidget = exports.createLeftAnchorWidget = function createLeftAnchorWidget(_ref) {
var _range$from;
var doc = _ref.doc,
from = _ref.from,
to = _ref.to,
diffId = _ref.diffId,
leftAnchorId = _ref.leftAnchorId,
measureElement = _ref.measureElement,
sliceOverride = _ref.sliceOverride,
underlyingRange = _ref.underlyingRange;
var overrideEdgeCases = sliceOverride ? getEdgeCasesForSlice(doc, from, sliceOverride, measureElement) : [];
var range = underlyingRange !== null && underlyingRange !== void 0 ? underlyingRange : to !== undefined ? {
from: from,
to: to
} : undefined;
var documentEdgeCases = getEdgeCasesForDocument(doc, (_range$from = range === null || range === void 0 ? void 0 : range.from) !== null && _range$from !== void 0 ? _range$from : from, range === null || range === void 0 ? void 0 : range.to);
var allEdgeCases = [].concat((0, _toConsumableArray2.default)(overrideEdgeCases), (0, _toConsumableArray2.default)(documentEdgeCases));
var edgeCase = (0, _fg.fg)('platform_editor_ai_show_diff_patch_2') ? allEdgeCases[0] : edgeCases(doc, from);
if (edgeCase === undefined) {
return undefined;
}
// Render the widget right before the doc-level node so it lives outside the
// resizable block container.
var beforePos = edgeCase.beforePos;
var leftAnchorKey = (0, _decorationKeys.buildAnchorDecorationKey)({
diffId: leftAnchorId !== null && leftAnchorId !== void 0 ? leftAnchorId : diffId,
anchorType: _decorationKeys.AnchorTypeKey.left
});
var leftResizeObserver;
return _view.Decoration.widget(beforePos, function (view, getPos) {
// Outer span stays in the flow but takes up no space.
var wrapper = document.createElement('div');
wrapper.style.setProperty('position', 'relative');
wrapper.style.setProperty('width', '100%');
// Inner span is absolutely positioned in the doc margin; it carries the
// `anchor-name` the IndicatorBar aligns its left edge against.
var anchor = document.createElement('div');
anchor.style.setProperty('anchor-name', "--".concat(leftAnchorKey));
anchor.style.setProperty('position', 'absolute');
anchor.style.setProperty('left', "calc(50% - ".concat((edgeCase === null || edgeCase === void 0 ? void 0 : edgeCase.leftOffset) || 0, "px)"));
// transforms on anchors have limited browser support
if (!(0, _fg.fg)('platform_editor_ai_show_diff_patch_2')) {
anchor.style.setProperty('transform', 'translateX(-50%)');
}
wrapper.appendChild(anchor);
var _measureWidth = function measureWidth() {
var _nodeDOM$querySelecto;
if ((0, _fg.fg)('platform_editor_ai_show_diff_patch_2')) {
var widthMeasureTargets = allEdgeCases.flatMap(function (target) {
var _target$widthMeasureT;
return (_target$widthMeasureT = target.widthMeasureTargets) !== null && _target$widthMeasureT !== void 0 ? _target$widthMeasureT : [target];
});
if (getPos() === undefined || widthMeasureTargets.length === 0) {
return;
}
var measuredElements = widthMeasureTargets.flatMap(function (_ref2) {
var leftOffset = _ref2.leftOffset,
targetMeasureElement = _ref2.measureElement,
measureLeft = _ref2.measureLeft,
measurePos = _ref2.measurePos,
measureSelector = _ref2.measureSelector;
if (measurePos === undefined) {
return [];
}
var nodeDOM = targetMeasureElement !== null && targetMeasureElement !== void 0 ? targetMeasureElement : view.nodeDOM(measurePos);
var elements = nodeDOM instanceof HTMLElement ? measureSelector ? Array.from(nodeDOM.querySelectorAll(measureSelector)) : [nodeDOM] : [];
return elements.map(function (element) {
return {
element: element,
leftOffset: leftOffset,
measureLeft: measureLeft
};
});
});
// A single diff can include multiple edge-case nodes, so measure the widest one.
var widest;
var _iterator = _createForOfIteratorHelper(measuredElements),
_step;
try {
for (_iterator.s(); !(_step = _iterator.n()).done;) {
var target = _step.value;
if (!widest || target.element.offsetWidth > widest.element.offsetWidth) {
widest = target;
}
}
} catch (err) {
_iterator.e(err);
} finally {
_iterator.f();
}
if (widest) {
var updateAnchor = function updateAnchor() {
if (getPos() === undefined) {
return;
}
if (widest.measureLeft) {
var left = widest.element.getBoundingClientRect().left - wrapper.getBoundingClientRect().left;
anchor.style.setProperty('left', "".concat(left, "px"));
} else {
var _widest$leftOffset;
anchor.style.setProperty('left', "calc(50% - ".concat((_widest$leftOffset = widest.leftOffset) !== null && _widest$leftOffset !== void 0 ? _widest$leftOffset : 0, "px - ").concat(widest.element.offsetWidth / 2, "px)"));
}
anchor.style.setProperty('width', "".concat(widest.element.offsetWidth, "px"));
};
updateAnchor();
// Observe the measured elements for size changes (e.g. page
// resize) so the indicator stays aligned.
if (!leftResizeObserver) {
leftResizeObserver = new ResizeObserver(_measureWidth);
measuredElements.forEach(function (_ref3) {
var _leftResizeObserver;
var element = _ref3.element;
return (_leftResizeObserver = leftResizeObserver) === null || _leftResizeObserver === void 0 ? void 0 : _leftResizeObserver.observe(element);
});
if (widest.measureLeft) {
leftResizeObserver.observe(wrapper);
}
}
}
return;
}
if (getPos() === undefined || edgeCase.measurePos === undefined) {
return;
}
var nodeDOM = view.nodeDOM(edgeCase.measurePos);
var dom = edgeCase.measureSelector && nodeDOM instanceof HTMLElement ? (_nodeDOM$querySelecto = nodeDOM.querySelector(edgeCase.measureSelector)) !== null && _nodeDOM$querySelecto !== void 0 ? _nodeDOM$querySelecto : nodeDOM : nodeDOM;
if (dom instanceof HTMLElement) {
var _updateAnchor = function _updateAnchor() {
if (getPos() === undefined) {
return;
}
if (edgeCase.measureLeft) {
var left = dom.getBoundingClientRect().left - wrapper.getBoundingClientRect().left;
anchor.style.setProperty('left', "".concat(left, "px"));
anchor.style.setProperty('transform', 'none');
}
anchor.style.setProperty('width', "".concat(dom.offsetWidth, "px"));
};
// Align against the block's horizontal extent.
_updateAnchor();
// Observe the measured element for size changes (e.g. page
// resize) so the indicator stays aligned. CCI-17981
if (!leftResizeObserver) {
leftResizeObserver = new ResizeObserver(_updateAnchor);
leftResizeObserver.observe(dom);
if (edgeCase.measureLeft) {
leftResizeObserver.observe(wrapper);
}
}
}
};
// The block DOM may not be settled synchronously (e.g. after a
// transaction), so defer the measurement like the gap cursor does.
requestAnimationFrame(_measureWidth);
return wrapper;
}, _objectSpread(_objectSpread({}, (0, _decorationKeys.buildAnchorDecorationSpec)({
diffId: diffId,
anchorType: _decorationKeys.AnchorTypeKey.left,
side: -999
})), {}, {
destroy: function destroy() {
var _leftResizeObserver2;
return (_leftResizeObserver2 = leftResizeObserver) === null || _leftResizeObserver2 === void 0 ? void 0 : _leftResizeObserver2.disconnect();
}
}));
};
/**
* Creates invisible anchor widgets for a single block-changed diff so that the
* `IndicatorBar` can use CSS anchor positioning to align itself with the diff.
*
* The interface mirrors `createInlineIndicatorAnchorWidgets`:
* - A `from` anchor is placed at the start of the node range (top of the bar).
* - A `to` anchor is placed at the end of the node range (bottom of the bar).
* - An optional `left` anchor is placed inside a resizable container (table,
* layout, expand) so the bar aligns within the container boundary.
*
*/
var createBlockIndicatorAnchorWidgets = exports.createBlockIndicatorAnchorWidgets = function createBlockIndicatorAnchorWidgets(_ref4) {
var _ref5, _parentTable$pos;
var doc = _ref4.doc,
from = _ref4.from,
to = _ref4.to,
diffId = _ref4.diffId,
leftAnchorId = _ref4.leftAnchorId;
var leftAnchor = createLeftAnchorWidget({
doc: doc,
from: from,
diffId: diffId,
leftAnchorId: leftAnchorId
});
var maybeLeftAnchor = leftAnchor ? [leftAnchor] : [];
/**
* A single anchor widget spans the full height of the block node, mimicking
* the gap cursor placement logic (see `place-gap-cursor.ts`): an element
* whose height is measured from the block's DOM so its box covers the block.
*
* Because the anchor rect covers the whole block, the `IndicatorBar` can
* resolve `top`, `bottom` and `left` against this one anchor (keyed by
* `diffId` with no `anchorType`) instead of separate `from`/`to` anchors.
*/
var blockAnchorKey = (0, _decorationKeys.buildAnchorDecorationKey)({
diffId: diffId
});
/**
* If `from` lands inside a table cell/header or a table row, the widget must
* still be rendered *outside* the table (widgets placed inside a table are
* clipped/mis-laid-out), but we want the anchor to be sized against the
* actual cell/row DOM. So we split into two positions:
* - `widgetPos`: where the widget DOM is rendered (outside the table).
* - `measurePos`: the closest cell/row whose DOM we measure for the height.
*/
var $from = doc.resolve(from);
var parentTable = (0, _utils.findParentNodeClosestToPos)($from, function (ancestor) {
return ancestor.type.name === 'table';
});
var parentCellOrRow = (0, _utils.findParentNodeClosestToPos)($from, function (ancestor) {
return ['tableCell', 'tableHeader', 'tableRow'].includes(ancestor.type.name);
});
var parentLayout = (0, _fg.fg)('platform_editor_ai_show_diff_patch_1') ? (0, _utils.findParentNodeClosestToPos)($from, function (ancestor) {
return ancestor.type.name === 'layoutSection';
}) : undefined;
// Layouts flatten direct widget children with display: contents. Such a wrapper has no
// bounding box or positioning context, so measuring its top produces an offset from the
// viewport origin and displaces the indicator into content below the editor. Keep the
// wrapper outside the layout, just as we do for tables, but still measure the target node.
var widgetPos = (_ref5 = (_parentTable$pos = parentTable === null || parentTable === void 0 ? void 0 : parentTable.pos) !== null && _parentTable$pos !== void 0 ? _parentTable$pos : parentLayout === null || parentLayout === void 0 ? void 0 : parentLayout.pos) !== null && _ref5 !== void 0 ? _ref5 : from;
// Measure the actual cell/row DOM when inside one; otherwise measure the
// widget's own position.
var measurePos = parentCellOrRow ? parentCellOrRow.pos : from;
var blockResizeObserver;
var blockWidget = _view.Decoration.widget(widgetPos, function (view, getPos) {
// Outer span stays in the flow but takes up no space.
var wrapper = document.createElement('span');
wrapper.style.setProperty('position', 'relative');
// Inner span is absolutely positioned and sized to the block height;
// it carries the `anchor-name` the IndicatorBar aligns against.
var anchor = document.createElement('span');
anchor.style.setProperty('position', 'absolute');
anchor.style.setProperty('anchor-name', "--".concat(blockAnchorKey));
wrapper.appendChild(anchor);
var _measureBlock = function measureBlock() {
if (getPos() === undefined) {
return;
}
var dom = view.nodeDOM(measurePos);
if (dom instanceof HTMLElement) {
anchor.style.setProperty('height', "".concat(dom.offsetHeight, "px"));
// The wrapper renders outside the table, so there is a vertical
// gap between it and the cell/row we're anchoring to. Measure
// that delta and offset the (absolutely positioned) anchor by it
// so its box lines up with the cell/row.
var wrapperTop = wrapper.getBoundingClientRect().top;
var domTop = dom.getBoundingClientRect().top;
var verticalOffset = domTop - wrapperTop;
anchor.style.setProperty('top', "".concat(verticalOffset, "px"));
// The offset already accounts for the cell/row's position, so the
// margin-top must not be double-applied.
anchor.style.setProperty('margin-top', '0px');
// Observe the measured element for size changes (e.g. page
// resize) so the indicator stays aligned. CCI-17981
if (!blockResizeObserver) {
blockResizeObserver = new ResizeObserver(function () {
if (getPos() !== undefined) {
_measureBlock();
}
});
blockResizeObserver.observe(dom);
}
}
};
// The block DOM may not be settled synchronously (e.g. after a
// transaction), so defer the measurement like the gap cursor does.
requestAnimationFrame(_measureBlock);
return wrapper;
}, _objectSpread(_objectSpread({}, (0, _decorationKeys.buildAnchorDecorationSpec)({
diffId: diffId,
// Reuse the `from` anchor type slot; the generated key intentionally
// omits the anchor type so the single element backs top/bottom/left.
anchorType: _decorationKeys.AnchorTypeKey.from,
side: -1
})), {}, {
destroy: function destroy() {
var _blockResizeObserver;
return (_blockResizeObserver = blockResizeObserver) === null || _blockResizeObserver === void 0 ? void 0 : _blockResizeObserver.disconnect();
}
}));
return [blockWidget].concat(maybeLeftAnchor);
};
/**
* A `from`/`to` on a `tableRow` boundary makes the anchor a direct `<tr>` (CSS grid)
* child, adding a phantom column that collapses the cells (EDITOR-8442). Clamp it
* inward into the neighbouring cell (`direction: 1` forward, `-1` back). Positions
* not on a row boundary are returned as-is.
*/
var clampAnchorPosIntoCell = exports.clampAnchorPosIntoCell = function clampAnchorPosIntoCell(doc, pos, direction) {
var $pos = doc.resolve(pos);
if ($pos.parent.type.name !== 'tableRow') {
return pos;
}
var cell = direction === 1 ? $pos.nodeAfter : $pos.nodeBefore;
if (!cell || cell.type.name !== 'tableHeader' && cell.type.name !== 'tableCell') {
return pos;
}
// +2 past the cell and its first child boundary = inside the cell's content;
// for the backward case, step back the same amount from the cell's end.
return direction === 1 ? pos + 2 : pos - 2;
};
/**
* A zero-size inline span carrying nothing but the `anchor-name` an overlay aligns against. A
* fragmented inline element reports the union of its line fragments, so aligning against one
* horizontally gives the enclosing block's content edge; a zero-size span is always a single
* fragment.
*/
var createAnchorNameSpan = exports.createAnchorNameSpan = function createAnchorNameSpan(anchorKey) {
var span = document.createElement('span');
span.style.setProperty('anchor-name', "--".concat(anchorKey));
return span;
};
var createAnchorSpanWidget = function createAnchorSpanWidget(_ref6) {
var pos = _ref6.pos,
diffId = _ref6.diffId,
anchorType = _ref6.anchorType,
side = _ref6.side;
return _view.Decoration.widget(pos, function () {
return createAnchorNameSpan((0, _decorationKeys.buildAnchorDecorationKey)({
diffId: diffId,
anchorType: anchorType
}));
}, (0, _decorationKeys.buildAnchorDecorationSpec)({
diffId: diffId,
anchorType: anchorType,
side: side
}));
};
/**
* Invisible `from`/`to` (and optional `left`) anchor widgets for one inline diff
* range, so the `IndicatorBar` can align itself via CSS anchor positioning.
*/
var createInlineIndicatorAnchorWidgets = exports.createInlineIndicatorAnchorWidgets = function createInlineIndicatorAnchorWidgets(_ref7) {
var doc = _ref7.doc,
from = _ref7.from,
to = _ref7.to,
diffId = _ref7.diffId,
leftAnchorId = _ref7.leftAnchorId;
var leftAnchor = createLeftAnchorWidget({
doc: doc,
from: from,
to: (0, _fg.fg)('platform_editor_ai_show_diff_patch_2') ? to : undefined,
diffId: diffId,
leftAnchorId: leftAnchorId
});
var maybeLeftAnchor = leftAnchor ? [leftAnchor] : [];
// Keep the start/end anchors out of the table row's grid (see helper above).
var fromPos = clampAnchorPosIntoCell(doc, from, 1);
var toPos = clampAnchorPosIntoCell(doc, to, -1);
/**
* Two widgets mark the start and end of the inline range so the
* IndicatorBar can determine top/bottom even if
* the inline decoration is broken up by marks / between blocks.
*/
// Gated purely so the `createAnchorSpanWidget` rewrite can be rolled back independently of the
// contributor-tag anchors it was extracted for: both branches build the same two widgets at the
// same positions. Drop the fallback with the gate.
if ((0, _fg.fg)('confluence_ncs_step_diffing_version_history')) {
return [createAnchorSpanWidget({
pos: fromPos,
diffId: diffId,
anchorType: _decorationKeys.AnchorTypeKey.from,
side: 1
}), createAnchorSpanWidget({
pos: toPos,
diffId: diffId,
anchorType: _decorationKeys.AnchorTypeKey.to,
side: -1
})].concat(maybeLeftAnchor);
}
var fromAnchorKey = (0, _decorationKeys.buildAnchorDecorationKey)({
diffId: diffId,
anchorType: _decorationKeys.AnchorTypeKey.from
});
var fromWidget = _view.Decoration.widget(fromPos, function () {
var span = document.createElement('span');
span.style.setProperty('anchor-name', "--".concat(fromAnchorKey));
return span;
}, (0, _decorationKeys.buildAnchorDecorationSpec)({
diffId: diffId,
anchorType: _decorationKeys.AnchorTypeKey.from,
side: 1
}));
var toAnchorKey = (0, _decorationKeys.buildAnchorDecorationKey)({
diffId: diffId,
anchorType: _decorationKeys.AnchorTypeKey.to
});
var toWidget = _view.Decoration.widget(toPos, function () {
var span = document.createElement('span');
span.style.setProperty('anchor-name', "--".concat(toAnchorKey));
return span;
}, (0, _decorationKeys.buildAnchorDecorationSpec)({
diffId: diffId,
anchorType: _decorationKeys.AnchorTypeKey.to,
side: -1
}));
return [fromWidget, toWidget].concat(maybeLeftAnchor);
};