@atlaskit/editor-plugin-show-diff
Version:
ShowDiff plugin for @atlaskit/editor-core
159 lines (155 loc) • 4.84 kB
JavaScript
/**
* Decoration families produced by the show-diff plugin.
* Each family owns its own key space and spec shape.
*/
export const DecorationFamily = {
diff: 'diff',
anchor: 'anchor',
contributorTag: 'contributor-tag'
};
/**
* Navigation measures the target table header and writes this variable on the
* editor root immediately before scrolling. Diff decorations inherit it, so
* native scrolling leaves room for the header without DOM mutation on the
* ProseMirror-rendered suggestion.
*/
export const SCROLL_TARGET_MARGIN_CSS_PROPERTY = '--ak-editor-diff-additional-margin';
export const scrollMarginTopValue = `calc(${"var(--ds-space-400, 32px)"} + var(${SCROLL_TARGET_MARGIN_CSS_PROPERTY}, 0px))`;
export const scrollMarginTopStyle = `scroll-margin-top: ${scrollMarginTopValue};`;
export const AnchorTypeKey = {
from: 'from',
to: 'to',
left: 'left',
docMargin: 'doc-margin',
/** The decorated block itself, which its contributor tag positions against. */
tag: 'tag'
};
/**
* The host widget one contributor tag renders into. Its own family, so the tag never reaches the
* consumers that read diff decorations — change counting, navigation and `getDeletedWidgets`.
*/
/**
* The diff-decoration kinds produced by the show-diff plugin. Each value is
* the leading segment of the generated key, so a diff decoration's kind can be
* matched with `key?.startsWith(DiffDecorationKey.inline)` etc.
*/
export const DiffDecorationKey = {
inline: `${DecorationFamily.diff}-inline`,
block: `${DecorationFamily.diff}-block`,
widget: `${DecorationFamily.diff}-widget`
};
export const AnchorDocMarginKey = `${DecorationFamily.anchor}-${AnchorTypeKey.docMargin}`;
export const buildAnchorDecorationKey = ({
diffId,
anchorType
}) => {
return `${DecorationFamily.anchor}-${diffId}${anchorType ? `-${anchorType}` : ''}`;
};
/**
* Builds a diff decoration key. The key includes the `diffId` so independently
* rendered decorations for the same type remain distinguishable.
*/
export const buildDiffDecorationKey = ({
decorationKeyPrefix,
isActive,
diffId
}) => `${decorationKeyPrefix}-${diffId}-${isActive ? 'active' : 'inactive'}`;
export const buildDiffDecorationSpec = ({
attributionKey,
colorScheme,
decorationType,
diffId,
isActive,
isInserted,
leftAnchorId,
nodeName,
side
}) => ({
decorationFamily: DecorationFamily.diff,
decorationType,
diffId,
key: buildDiffDecorationKey({
decorationKeyPrefix: DiffDecorationKey[decorationType],
diffId,
isActive
}),
...(attributionKey ? {
attributionKey
} : {}),
// Active state is only needed by contributor tags, so keep it alongside an attribution key.
...(attributionKey && isActive !== undefined ? {
isActive
} : {}),
...(isInserted !== undefined ? {
isInserted
} : {}),
...(leftAnchorId ? {
leftAnchorId
} : {}),
...(colorScheme ? {
colorScheme
} : {}),
...(nodeName ? {
nodeName
} : {}),
...(side !== undefined ? {
side
} : {})
});
export function buildAnchorDecorationSpec({
anchorType,
diffId,
side
}) {
if (anchorType === AnchorTypeKey.docMargin) {
return {
decorationFamily: DecorationFamily.anchor,
anchorType,
key: AnchorDocMarginKey,
...(side !== undefined ? {
side
} : {})
};
}
if (!diffId) {
throw new Error(`diffId is required for anchor type "${anchorType}"`);
}
return {
decorationFamily: DecorationFamily.anchor,
anchorType,
diffId,
key: buildAnchorDecorationKey({
diffId,
anchorType
}),
...(side !== undefined ? {
side
} : {})
};
}
export const buildContributorTagDecorationSpec = diffId => ({
decorationFamily: DecorationFamily.contributorTag,
diffId,
// Stable across recalculations, so ProseMirror keeps the host element the tag is portalled into.
key: `${DecorationFamily.contributorTag}-${diffId}`
});
export const isDiffDecorationSpec = spec => Boolean(spec && typeof spec === 'object' && 'decorationFamily' in spec && spec.decorationFamily === DecorationFamily.diff);
export const isAnchorDecorationSpec = spec => Boolean(spec && typeof spec === 'object' && 'decorationFamily' in spec && spec.decorationFamily === DecorationFamily.anchor);
export const isDiffDecoration = decoration => isDiffDecorationSpec(decoration.spec);
export const extractDiffDescriptors = decorations => decorations.find(undefined, undefined, isDiffDecorationSpec).filter(isDiffDecoration).map(({
spec
}) => {
return {
...(spec.colorScheme ? {
colorScheme: spec.colorScheme
} : {}),
id: spec.diffId,
...(spec.leftAnchorId ? {
leftAnchorId: spec.leftAnchorId
} : {}),
...(spec.isInserted !== undefined ? {
isInserted: spec.isInserted
} : {}),
type: spec.decorationType
};
});