UNPKG

@atlaskit/editor-plugin-show-diff

Version:

ShowDiff plugin for @atlaskit/editor-core

289 lines (274 loc) • 16.1 kB
"use strict"; var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault"); Object.defineProperty(exports, "__esModule", { value: true }); exports.getAtomicInlineChangedAttrs = exports.createInlineChangedDecoration = void 0; var _toConsumableArray2 = _interopRequireDefault(require("@babel/runtime/helpers/toConsumableArray")); var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty")); var _lazyNodeView = require("@atlaskit/editor-common/lazy-node-view"); var _view = require("@atlaskit/editor-prosemirror/view"); var _isExperimentEnabled = require("@atlaskit/platform-feature-experiments/is-experiment-enabled"); var _fg = require("@atlaskit/platform-feature-flags/fg"); var _buildContributorTagDom = require("../../ui/ContributorTag/buildContributorTagDom"); var _isEmptyParagraphSlice = require("../utils/isEmptyParagraphSlice"); var _factory = require("./colorSchemes/factory"); var _schemes = require("./colorSchemes/schemes"); var _createAnchorDecorationWidgets = require("./createAnchorDecorationWidgets"); var _createContributorTagWidget = require("./createContributorTagWidget"); var _createInlineChangedDecorationStyles = require("./createInlineChangedDecoration.styles.legacy"); var _decorationKeys = require("./decorationKeys"); var _revealStyles = require("./revealStyles"); 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; } var displayNoneStyle = (0, _lazyNodeView.convertToInlineCss)({ display: 'none' }); /** * Stacks the change's highlight below its contributor tag, so no highlight can slice through a tag: * a tag overlays the line *above* its own change, and a highlight already on that line — the tail of * this change, or another change entirely — would otherwise paint straight over it. * * The tag still reads as attached to the change rather than laid on top of it, because it cannot * paint over this highlight at all: its bottom edge is pinned to the highlight's top edge and * clipped there. See `CONTRIBUTOR_TAG_Z_INDEX`. * * prosemirror-view places the tag's host widget as a *sibling* of this decoration's span, so * painting order is decided by stacking level — and the host is absolutely positioned at * `CONTRIBUTOR_TAG_Z_INDEX`. `position: relative` is what makes `z-index` apply to an otherwise * static inline box; it adds no offset. */ var stackBelowContributorTagStyle = (0, _lazyNodeView.convertToInlineCss)({ position: 'relative', zIndex: _buildContributorTagDom.CONTRIBUTOR_TAG_Z_INDEX - 1 }); var getColorScheme = function getColorScheme(colorScheme) { return _schemes.colorSchemeRegistry[colorScheme !== null && colorScheme !== void 0 ? colorScheme : 'standard']; }; /** * Class names for an atomic inline node decoration (date, emoji, mention, status). Scheme-agnostic * — `buildAtomicInlineChangedCSSVariables()` supplies the colour inline on the same element. */ /** * Lets editor-core redraw the strike on a deleted status: `line-through` from an ancestor paints * behind the lozenge's own background (EDITOR-9123). Not needed for deleted-content widgets — * `createContentWrapper` appends a real positioned line. */ var DELETED_INLINE_NODE_CLASS = 'show-diff-deleted-inline-node'; var getAtomicInlineNodeClassName = function getAtomicInlineNodeClassName(inlineNodeName) { var classNames = ['show-diff-atomic-inline-changed']; if (inlineNodeName) { classNames.push("show-diff-atomic-inline-changed-".concat(inlineNodeName)); } return classNames.join(' '); }; /** Inline style for inserted content. */ var getExtendedInsertStyle = function getExtendedInsertStyle(colors, isActive, hideAddedDiffsUnderline) { if (colors.insertedInlineTreatment === 'underline') { // hideAddedDiffsUnderline only targets the 'borderBottom' rule, so it does not apply here. return isActive ? (0, _factory.buildInsertStyleActive)(colors) : (0, _factory.buildInsertStyle)(colors); } if (isActive) { return hideAddedDiffsUnderline ? (0, _factory.buildInsertStyleExtendedNoUnderlineActive)(colors) : (0, _factory.buildInsertStyleExtendedActive)(colors); } return hideAddedDiffsUnderline ? (0, _factory.buildInsertStyleExtendedNoUnderline)(colors) : (0, _factory.buildInsertStyleExtended)(colors); }; /** Inline style for deleted content. */ var getExtendedDeletedStyle = function getExtendedDeletedStyle(colors, isActive) { if (colors.deletedInlineTreatment === 'strikethrough') { // Identical in both states — the block decoration carries the active emphasis. return (0, _factory.buildDeletedInlineStyle)(colors, false); } return (0, _factory.buildDeletedInlineStyleStandard)(colors, isActive ? 'active' : 'default') + (0, _factory.buildDeletedInlineContentStyleExtended)(colors, isActive); }; /** Registry-driven inline `style` for a changed-content decoration. */ var resolveInlineChangedStyleRefactored = function resolveInlineChangedStyleRefactored(_ref) { var colors = _ref.colors, hideAddedDiffsUnderline = _ref.hideAddedDiffsUnderline, isActive = _ref.isActive, isInserted = _ref.isInserted; return isInserted ? getExtendedInsertStyle(colors, isActive, hideAddedDiffsUnderline) : getExtendedDeletedStyle(colors, isActive); }; /** * Single gate for the inline `style` string: registry + factory when the refactor is on, the * verbatim pre-refactor per-scheme constants when it is off. */ var resolveInlineChangedStyle = function resolveInlineChangedStyle(args) { return (0, _isExperimentEnabled.isExperimentEnabled)('platform_editor_show_diff_color_scheme_refactor') ? resolveInlineChangedStyleRefactored(args) : (0, _createInlineChangedDecorationStyles.resolveInlineChangedStyleLegacy)(_objectSpread(_objectSpread({}, args), {}, { colorScheme: (0, _schemes.getLegacyColorScheme)(args.colorScheme) })); }; /** * Single gate for the atomic-inline-node decoration attributes. The two cohorts differ in the DOM: * on, the colour rides on an inline CSS variable and the class list is scheme-agnostic; off, the * colour comes from a `-traditional` class and no variable is emitted. Returned together so the * class list and style suffix can never be mixed across cohorts. */ var resolveAtomicInlineAttrs = function resolveAtomicInlineAttrs(inlineNodeName, colorScheme, colors) { return (0, _isExperimentEnabled.isExperimentEnabled)('platform_editor_show_diff_color_scheme_refactor') ? { className: getAtomicInlineNodeClassName(inlineNodeName), styleSuffix: (0, _factory.buildAtomicInlineChangedCSSVariables)(colors) } : { className: (0, _createInlineChangedDecorationStyles.getAtomicInlineNodeClassNameLegacy)(inlineNodeName, (0, _schemes.getLegacyColorScheme)(colorScheme)), styleSuffix: '' }; }; var getAtomicInlineChangedAttrs = exports.getAtomicInlineChangedAttrs = function getAtomicInlineChangedAttrs(inlineNodeName, colorScheme) { return resolveAtomicInlineAttrs(inlineNodeName, colorScheme, getColorScheme(colorScheme)); }; /** * Inline decoration used for insertions as the content already exists in the document * * @param change Changeset "change" containing information about the change content + range * @returns Prosemirror inline decoration */ var createInlineChangedDecoration = exports.createInlineChangedDecoration = function createInlineChangedDecoration(_ref2) { var _revealed$style; var attributionKey = _ref2.attributionKey, change = _ref2.change, colorScheme = _ref2.colorScheme, _ref2$isActive = _ref2.isActive, isActive = _ref2$isActive === void 0 ? false : _ref2$isActive, _ref2$isInserted = _ref2.isInserted, isInserted = _ref2$isInserted === void 0 ? true : _ref2$isInserted, leftAnchorId = _ref2.leftAnchorId, _ref2$isAtomicInlineN = _ref2.isAtomicInlineNode, isAtomicInlineNode = _ref2$isAtomicInlineN === void 0 ? false : _ref2$isAtomicInlineN, _ref2$shouldHideDelet = _ref2.shouldHideDeleted, shouldHideDeleted = _ref2$shouldHideDelet === void 0 ? false : _ref2$shouldHideDelet, _ref2$showContributor = _ref2.showContributorTags, showContributorTags = _ref2$showContributor === void 0 ? false : _ref2$showContributor, _ref2$showIndicators = _ref2.showIndicators, showIndicators = _ref2$showIndicators === void 0 ? false : _ref2$showIndicators, doc = _ref2.doc, _ref2$hideAddedDiffsU = _ref2.hideAddedDiffsUnderline, hideAddedDiffsUnderline = _ref2$hideAddedDiffsU === void 0 ? false : _ref2$hideAddedDiffsU, inlineNodeName = _ref2.inlineNodeName, _ref2$hasDeletedWidge = _ref2.hasDeletedWidget, hasDeletedWidget = _ref2$hasDeletedWidge === void 0 ? false : _ref2$hasDeletedWidge, _ref2$isDeletedWidget = _ref2.isDeletedWidgetBelow, isDeletedWidgetBelow = _ref2$isDeletedWidget === void 0 ? false : _ref2$isDeletedWidget, reveal = _ref2.reveal, tagMountContext = _ref2.tagMountContext; // Derived from the range so it survives a recalculation: stepping to the next/previous change // rebuilds decorations without touching the doc, and a fresh id would change the React key and the // tag host's key, remounting every tag mid-step. Random ids are kept when tags are off. var diffId = showContributorTags ? "inline-".concat(change.fromB, "-").concat(change.toB) : crypto.randomUUID(); // Match the rendered shape: whitespace text is eligible, while an empty paragraph is structural // content with no tag host. var canTagChange = showContributorTags && !!doc && (0, _createContributorTagWidget.isContributorTagWidgetEnabled)() && !(0, _isEmptyParagraphSlice.isEmptyParagraphSlice)(doc.slice(change.fromB, change.toB)); if (shouldHideDeleted) { return [_view.Decoration.inline(change.fromB, change.toB, { style: displayNoneStyle }, (0, _decorationKeys.buildDiffDecorationSpec)({ // No attribution: this decoration paints nothing and emits no tag host, so a tag // resolved from it would have nowhere to render. colorScheme: colorScheme, decorationType: 'inline', diffId: diffId, leftAnchorId: leftAnchorId, isActive: isActive, isInserted: isInserted }))]; } var colors = getColorScheme(colorScheme); // The reveal withholds the static highlight so the wipe has something to reveal, so it supplies // the whole style rather than adding to the usual one. var revealed = (0, _revealStyles.resolveRevealStyle)({ colorScheme: colorScheme, hideAddedDiffsUnderline: hideAddedDiffsUnderline, isActive: isActive, isInserted: isInserted, reveal: reveal }); var style = (_revealed$style = revealed === null || revealed === void 0 ? void 0 : revealed.style) !== null && _revealed$style !== void 0 ? _revealed$style : resolveInlineChangedStyle({ colors: colors, colorScheme: colorScheme, hideAddedDiffsUnderline: hideAddedDiffsUnderline, isActive: isActive, isInserted: isInserted }); var isAtomicInlineInsertion = isAtomicInlineNode && isInserted; var atomicInlineAttrs = isAtomicInlineInsertion ? resolveAtomicInlineAttrs(inlineNodeName, colorScheme, colors) : undefined; // Every deletion: an inverted diff carries no `isAtomicInlineNode`, so editor-core's selectors do // the matching. var deletedInlineNodeAttrs = !isInserted && (0, _fg.fg)('platform_editor_ai_show_diff_patch_2') ? { className: DELETED_INLINE_NODE_CLASS, styleSuffix: (0, _factory.buildDeletedInlineNodeCSSVariables)(colors, isActive) } : undefined; // Built from the raw change range, not the indicator anchors resolved below: a tag lines up with // the changed text, while the bar's anchors are held on a block boundary when a deleted widget // renders beside them. // // Built up here, ahead of where it is pushed, so the highlight's stacking below keys off whether a // tag exists rather than re-deriving the condition and risking disagreement. var tagWidget = canTagChange && doc ? (0, _createContributorTagWidget.createContributorTagWidget)({ doc: doc, from: change.fromB, to: change.toB, diffId: diffId, mountContext: tagMountContext }) : undefined; var inlineStyle = [style, atomicInlineAttrs === null || atomicInlineAttrs === void 0 ? void 0 : atomicInlineAttrs.styleSuffix, deletedInlineNodeAttrs === null || deletedInlineNodeAttrs === void 0 ? void 0 : deletedInlineNodeAttrs.styleSuffix, tagWidget ? stackBelowContributorTagStyle : undefined, (0, _fg.fg)('platform_editor_ai_show_diff_patch_1') ? _decorationKeys.scrollMarginTopStyle : undefined].filter(Boolean).join(''); var decorations = [_view.Decoration.inline(change.fromB, change.toB, _objectSpread(_objectSpread(_objectSpread(_objectSpread({ style: inlineStyle }, atomicInlineAttrs && { class: atomicInlineAttrs.className }), deletedInlineNodeAttrs && { class: deletedInlineNodeAttrs.className }), revealed && (0, _defineProperty2.default)({}, _revealStyles.REVEAL_ATTR, revealed.role)), {}, { 'data-testid': 'show-diff-changed-decoration' }, (showContributorTags || (0, _fg.fg)('platform_editor_ai_show_diff_patch_2')) && { 'data-diff-id': diffId }), (0, _decorationKeys.buildDiffDecorationSpec)({ attributionKey: canTagChange ? attributionKey : undefined, colorScheme: colorScheme, decorationType: 'inline', diffId: diffId, leftAnchorId: leftAnchorId, isActive: isActive, isInserted: isInserted }))]; if (showIndicators && doc) { // For paragraphs, fromB/toB land on the outer block // boundary. Adjust anchor widgets into inline content so the indicator // bar doesn't extend into the preceding block's margin. // Skip when the deleted-content widget is rendered — the anchor must // stay at the boundary to keep the indicator continuous. // // The widget only pins the end of the range it is anchored to. It sits at `change.fromB` // by default, but `deletedDiffPlacement: 'bottom'` / `inlineDeletedDiffPlacement: 'after'` // move it to `change.toB` so the deleted content reads after the added content. In that // case the `from` anchor has no widget beside it any more and must be adjusted inward // again, otherwise the over-long indicator bar that CCI-18224 fixed comes back. var anchorFrom = change.fromB; var anchorTo = change.toB; if (!hasDeletedWidget || isDeletedWidgetBelow) { var _$from$nodeAfter; var $from = doc.resolve(anchorFrom); if (!$from.parent.inlineContent && (_$from$nodeAfter = $from.nodeAfter) !== null && _$from$nodeAfter !== void 0 && _$from$nodeAfter.isTextblock) { anchorFrom = anchorFrom + 1; } } if (!hasDeletedWidget) { var _$to$nodeBefore; var $to = doc.resolve(anchorTo); if (!$to.parent.inlineContent && (_$to$nodeBefore = $to.nodeBefore) !== null && _$to$nodeBefore !== void 0 && _$to$nodeBefore.isTextblock) { anchorTo = anchorTo - 1; } } decorations.push.apply(decorations, (0, _toConsumableArray2.default)((0, _createAnchorDecorationWidgets.createInlineIndicatorAnchorWidgets)({ doc: doc, from: anchorFrom, to: anchorTo, diffId: diffId, leftAnchorId: leftAnchorId }))); } // Pushed after the indicator anchors so the decoration order is unchanged — see `tagWidget` above // for why it is built before them. if (tagWidget) { decorations.push.apply(decorations, (0, _toConsumableArray2.default)(tagWidget)); } return decorations; };