UNPKG

@atlaskit/editor-plugin-show-diff

Version:

ShowDiff plugin for @atlaskit/editor-core

99 lines (93 loc) 3.58 kB
/** * Single source of truth for which attributes of a node type represent a * meaningful change. Shared by `attrAwareTokenEncoder` (changeset detection) * and `getAttrChangeRanges` (decoration rendering) so the two stages agree. */ // `localId` is editor bookkeeping rather than user-visible content. Suggestions can regenerate it, // so treating it as a diff would create phantom changes. const DEFAULT_EXCLUDED_ATTRS = ['localId']; // Specialised rules keep existing node-specific behaviour. Node types absent from this map use the // generic fallback and diff every attribute except editor bookkeeping attributes. export const DIFFABLE_ATTRS_BY_NODE_TYPE = { tableCell: { include: ['background', 'colspan', 'rowspan'] }, tableHeader: { include: ['background', 'colspan', 'rowspan'] }, // Inline nodes whose identity is their attrs. date: { include: ['timestamp'] }, emoji: { include: ['shortName', 'id', 'text'] }, mention: { include: ['id', 'text'] }, status: { include: ['text', 'color'] }, taskItem: { include: ['state'] }, // Media: id/collection/url identify the image. media: { include: ['id', 'collection', 'url'] }, // Panel: type/colour/icon change is formatting-only but reviewable. Keyed by base // node type — the encoder normalises variants (e.g. `panel_c1`) to `panel` first. panel: { include: ['panelType', 'panelColor', 'panelIcon', 'panelIconId', 'panelIconText'] }, // Extensions: any attribute change is meaningful except localId. extension: { exclude: ['localId'] }, inlineExtension: { exclude: ['localId'] }, bodiedExtension: { exclude: ['localId'] } }; // Whether `attrName` is a meaningful change for `nodeTypeName`. Nodes without a specialised rule // diff all attributes except editor bookkeeping attributes. export const isDiffableAttr = (nodeTypeName, attrName) => { const rule = DIFFABLE_ATTRS_BY_NODE_TYPE[nodeTypeName]; if (!rule) { return !DEFAULT_EXCLUDED_ATTRS.includes(attrName); } return 'include' in rule ? rule.include.includes(attrName) : !rule.exclude.includes(attrName); }; // The fixed include list for a node type, or `undefined` for no/exclude rule. // Use where a node-independent attr list is needed (e.g. the inline node map). export const getIncludedDiffableAttrs = nodeTypeName => { const rule = DIFFABLE_ATTRS_BY_NODE_TYPE[nodeTypeName]; return rule && 'include' in rule ? rule.include : undefined; }; // Like `getIncludedDiffableAttrs` but asserts the include rule exists, so a // consumer relying on a fixed list fails loudly if the rule is removed or // switched to an exclude rule rather than silently drifting. export const getRequiredIncludedDiffableAttrs = nodeTypeName => { const attrs = getIncludedDiffableAttrs(nodeTypeName); if (!attrs) { throw new Error(`diffableAttrs: expected an include rule for "${nodeTypeName}"`); } return attrs; }; /** * Concrete attr names to diff for a node instance, honouring specialised rules and otherwise * including every attribute except editor bookkeeping. Attribute names are sorted because key * order is not stable across the two diffed documents. */ export const getDiffableAttrNames = (nodeTypeName, attrs) => { const rule = DIFFABLE_ATTRS_BY_NODE_TYPE[nodeTypeName]; if (!rule) { return Object.keys(attrs).filter(name => !DEFAULT_EXCLUDED_ATTRS.includes(name)).sort(); } if ('include' in rule) { return rule.include; } return Object.keys(attrs).filter(name => !rule.exclude.includes(name)).sort(); };