@atlaskit/editor-plugin-show-diff
Version:
ShowDiff plugin for @atlaskit/editor-core
215 lines (204 loc) • 10.9 kB
JavaScript
;
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.stepIsValidAttrChange = exports.isInlineAttrChangeNodeName = exports.getAttrChangeRanges = void 0;
var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty"));
var _isEqual = _interopRequireDefault(require("lodash/isEqual"));
var _omit = _interopRequireDefault(require("lodash/omit"));
var _setAttrs = require("@atlaskit/adf-schema/steps/set-attrs");
var _nodeTypeUtils = require("@atlaskit/editor-common/utils/node-type-utils");
var _transform = require("@atlaskit/editor-prosemirror/transform");
var _fg = require("@atlaskit/platform-feature-flags/fg");
var _diffableAttrs = require("./diffableAttrs");
var _excerptIncludeAttrs = require("./excerptIncludeAttrs");
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 filterUndefined = function filterUndefined(x) {
return !!x;
};
// Attr lists sourced from the shared `diffableAttrs` map (shared with the encoder).
var mediaAttrs = (0, _diffableAttrs.getRequiredIncludedDiffableAttrs)('media');
var dateAttrs = (0, _diffableAttrs.getRequiredIncludedDiffableAttrs)('date');
var taskItemAttrs = (0, _diffableAttrs.getRequiredIncludedDiffableAttrs)('taskItem');
var panelAttrs = (0, _diffableAttrs.getRequiredIncludedDiffableAttrs)('panel');
var emojiAttrs = (0, _diffableAttrs.getRequiredIncludedDiffableAttrs)('emoji');
var mentionAttrs = (0, _diffableAttrs.getRequiredIncludedDiffableAttrs)('mention');
var statusAttrs = (0, _diffableAttrs.getRequiredIncludedDiffableAttrs)('status');
// Map of node type name → the attrs that represent a meaningful content change for that node
var inlineNodeAttrMap = {
date: dateAttrs,
emoji: emojiAttrs,
mention: mentionAttrs,
status: statusAttrs
};
var isInlineAttrChangeNodeName = exports.isInlineAttrChangeNodeName = function isInlineAttrChangeNodeName(nodeName) {
return nodeName in inlineNodeAttrMap;
};
// Extension node type names. Their "any attr except localId" exclude rule lives
// in the shared `diffableAttrs` map and is applied via `isDiffableAttr` below.
var extensionNodeNames = ['extension', 'inlineExtension', 'bodiedExtension'];
var transientExtensionAttrPaths = ['localId', 'parameters.macroParams._parentId'];
/**
* Removes extension metadata that can change between document versions without changing the
* extension's user-visible configuration.
*/
var getComparableExtensionAttrs = function getComparableExtensionAttrs(node) {
return (0, _omit.default)(node.attrs, transientExtensionAttrPaths);
};
var haveSameRelevantExtensionAttrs = function haveSameRelevantExtensionAttrs(beforeNode, afterNode) {
if ((0, _fg.fg)('platform_editor_normalize_excerpt_diff') && (0, _excerptIncludeAttrs.isExcerptInclude)(beforeNode.attrs) && (0, _excerptIncludeAttrs.isExcerptInclude)(afterNode.attrs)) {
var beforeReference = (0, _excerptIncludeAttrs.getReferencedContentId)(beforeNode.attrs);
var afterReference = (0, _excerptIncludeAttrs.getReferencedContentId)(afterNode.attrs);
return (0, _isEqual.default)((0, _excerptIncludeAttrs.getComparableExcerptIncludeAttrs)(beforeNode.attrs), (0, _excerptIncludeAttrs.getComparableExcerptIncludeAttrs)(afterNode.attrs)) && (beforeReference === undefined || afterReference === undefined || beforeReference === afterReference);
}
return (0, _isEqual.default)(getComparableExtensionAttrs(beforeNode), getComparableExtensionAttrs(afterNode));
};
var getStepAttrs = function getStepAttrs(step) {
if (step instanceof _transform.AttrStep) {
return [step.attr];
}
if (step.attrs) {
return Object.keys(step.attrs);
}
return [];
};
var getAttrChangeRanges = exports.getAttrChangeRanges = function getAttrChangeRanges(doc, attrStepContexts, originalDoc) {
return attrStepContexts.map(function (attrStepContext) {
var afterNode = attrStepContext.afterNode,
attributionKey = attrStepContext.attributionKey,
beforeNode = attrStepContext.beforeNode,
finalPos = attrStepContext.finalPos,
originalPos = attrStepContext.originalPos,
step = attrStepContext.step;
if (!(step instanceof _transform.AttrStep) && !(step instanceof _setAttrs.SetAttrsStep)) {
return undefined;
}
var stepAttrs = getStepAttrs(step);
// SetAttrsStep contains the complete replacement attrs, not only the attrs that changed,
// so narrow it to the attrs whose values actually differ. Fall back to the full step
// payload if either step-time node is unavailable.
var attrsToCheck = beforeNode && afterNode ? stepAttrs.filter(function (attrName) {
return !(0, _isEqual.default)(beforeNode.attrs[attrName], afterNode.attrs[attrName]);
}) : stepAttrs;
var $pos = doc.resolve(finalPos);
var nodeAtPos = doc.nodeAt(finalPos);
var originalNodeAtPos = originalPos === undefined ? null : originalDoc.nodeAt(originalPos);
var hasChangedAttr = function hasChangedAttr(watchedAttrs) {
return attrsToCheck.some(function (attr) {
return watchedAttrs.includes(attr) && (!(0, _fg.fg)('confluence_ncs_step_diffing_version_history') || !originalNodeAtPos || originalNodeAtPos.type !== (nodeAtPos === null || nodeAtPos === void 0 ? void 0 : nodeAtPos.type) || !(0, _isEqual.default)(originalNodeAtPos.attrs[attr], nodeAtPos === null || nodeAtPos === void 0 ? void 0 : nodeAtPos.attrs[attr]));
});
};
// The changeset path (createDecorationsForChange) handles the deletion widget via
// prosemirror-changeset; we only need to add the inline insertion highlight here.
if (nodeAtPos) {
var nodeName = nodeAtPos.type.name;
if (isInlineAttrChangeNodeName(nodeName)) {
var watchedAttrs = inlineNodeAttrMap[nodeName];
if (hasChangedAttr(watchedAttrs)) {
return _objectSpread(_objectSpread({}, attributionKey ? {
attributionKey: attributionKey
} : {}), {}, {
fromB: finalPos,
toB: finalPos + nodeAtPos.nodeSize,
isInline: true,
inlineNodeName: nodeName
}, originalPos !== undefined && originalNodeAtPos && {
fromA: originalPos,
toA: originalPos + originalNodeAtPos.nodeSize
});
}
}
}
// taskItem node: state attribute change — highlight the taskItem node
if (attrsToCheck.some(function (v) {
return taskItemAttrs.includes(v);
}) && (nodeAtPos === null || nodeAtPos === void 0 ? void 0 : nodeAtPos.type.name) === 'taskItem') {
return _objectSpread(_objectSpread({}, attributionKey ? {
attributionKey: attributionKey
} : {}), {}, {
fromB: finalPos,
toB: finalPos + nodeAtPos.nodeSize
});
}
// panel node (incl. variants like panel_c1): type/colour/icon attribute change
// (e.g. note -> warning) — highlight the new panel and, when the original node
// is resolvable, expose its range (fromA/toA) so the caller can render the old
// panel as a "deleted" widget for a before/after comparison.
if (hasChangedAttr(panelAttrs) && nodeAtPos && (0, _nodeTypeUtils.getBaseNodeTypeName)(nodeAtPos.type) === 'panel') {
return _objectSpread(_objectSpread({}, attributionKey ? {
attributionKey: attributionKey
} : {}), {}, {
fromB: finalPos,
toB: finalPos + nodeAtPos.nodeSize
}, originalPos !== undefined && originalNodeAtPos && {
fromA: originalPos,
toA: originalPos + originalNodeAtPos.nodeSize
});
}
// Extension nodes: highlight changes to user-visible configuration. Transient extension
// metadata is ignored by the comparison below.
if (nodeAtPos && extensionNodeNames.includes(nodeAtPos.type.name) && attrsToCheck.some(function (v) {
return (0, _diffableAttrs.isDiffableAttr)(nodeAtPos.type.name, v);
})) {
if (beforeNode && afterNode && beforeNode.type === afterNode.type && haveSameRelevantExtensionAttrs(beforeNode, afterNode)) {
return undefined;
}
var isInline = nodeAtPos.type.name === 'inlineExtension';
return _objectSpread(_objectSpread({}, attributionKey ? {
attributionKey: attributionKey
} : {}), {}, {
fromB: finalPos,
toB: finalPos + nodeAtPos.nodeSize,
isInline: isInline
});
}
// media node: id/collection/url attribute change — highlight the mediaSingle parent
if (attrsToCheck.some(function (v) {
return mediaAttrs.includes(v);
}) && $pos.parent.type === doc.type.schema.nodes.mediaSingle) {
var startPos = $pos.pos + $pos.parentOffset;
return _objectSpread(_objectSpread({}, attributionKey ? {
attributionKey: attributionKey
} : {}), {}, {
fromB: startPos,
toB: startPos + $pos.parent.nodeSize - 1
});
}
return undefined;
}).filter(filterUndefined)
// Deduplicate by node position: multiple AttrSteps on the same node
// (e.g. setNodeAttribute(pos, 'text', ...) + setNodeAttribute(pos, 'color', ...))
// should produce only one decoration, not one per step.
.filter(function (range, i, arr) {
return (0, _fg.fg)('confluence_ncs_step_diffing_version_history') ? !arr.some(function (r, nextIndex) {
return nextIndex > i && r.fromB === range.fromB;
}) : arr.findIndex(function (r) {
return r.fromB === range.fromB;
}) === i;
});
};
/**
* Check if the step was a valid attr change and affected the doc
*
* @param step Attr step to test
* @param beforeDoc Doc before the step
* @param afterDoc Doc after the step
* @returns Boolean if the change should show a decoration
*/
var stepIsValidAttrChange = exports.stepIsValidAttrChange = function stepIsValidAttrChange(step, beforeDoc, afterDoc) {
try {
if (step instanceof _transform.AttrStep || step instanceof _setAttrs.SetAttrsStep) {
var attrStepAfter = afterDoc.nodeAt(step.pos);
var attrStepBefore = beforeDoc.nodeAt(step.pos);
// The change affected the document
if (attrStepAfter && attrStepBefore && !attrStepAfter.eq(attrStepBefore)) {
return true;
}
}
return false;
} catch (_unused) {
return false;
}
};