react-diff-viewer-continued
Version:
Continuation of a simple and beautiful text diff viewer component made with diff and React
420 lines (419 loc) • 12.6 kB
JavaScript
import createEmotion from "@emotion/css/create-instance";
export default (styleOverride, useDarkTheme = false, nonce = "") => {
const { variables: overrideVariables = {}, ...styles } = styleOverride;
const themeVariables = {
light: {
...{
diffViewerBackground: "#fff",
diffViewerColor: "#212529",
addedBackground: "#e6ffed",
addedColor: "#24292e",
removedBackground: "#ffeef0",
removedColor: "#24292e",
changedBackground: "#fffbdd",
wordAddedBackground: "#acf2bd",
wordRemovedBackground: "#fdb8c0",
addedGutterBackground: "#cdffd8",
removedGutterBackground: "#ffdce0",
gutterBackground: "#f7f7f7",
gutterBackgroundDark: "#f3f1f1",
highlightBackground: "#fffbdd",
highlightGutterBackground: "#fff5b1",
codeFoldGutterBackground: "#dbedff",
codeFoldBackground: "#f1f8ff",
emptyLineBackground: "#fafbfc",
gutterColor: "#212529",
addedGutterColor: "#212529",
removedGutterColor: "#212529",
codeFoldContentColor: "#212529",
diffViewerTitleBackground: "#fafbfc",
diffViewerTitleColor: "#212529",
diffViewerTitleBorderColor: "#eee",
},
...(overrideVariables.light || {}),
},
dark: {
...{
diffViewerBackground: "#2e303c",
diffViewerColor: "#FFF",
addedBackground: "#2ea04326",
addedColor: "white",
removedBackground: "#f851491a",
removedColor: "white",
changedBackground: "#3e302c",
wordAddedBackground: "#2ea04366",
wordRemovedBackground: "#f8514966",
addedGutterBackground: "#3fb9504d",
removedGutterBackground: "#f851494d",
gutterBackground: "#2c2f3a",
gutterBackgroundDark: "#262933",
highlightBackground: "#2a3967",
highlightGutterBackground: "#2d4077",
codeFoldGutterBackground: "#262831",
codeFoldBackground: "#262831",
emptyLineBackground: "#363946",
gutterColor: "#f0f6fc",
addedGutterColor: "#f0f6fc",
removedGutterColor: "#f0f6fc",
codeFoldContentColor: "#9198a1",
diffViewerTitleBackground: "#2f323e",
diffViewerTitleColor: "#f0f6fc",
diffViewerTitleBorderColor: "#353846",
},
...(overrideVariables.dark || {}),
},
};
const variables = useDarkTheme ? themeVariables.dark : themeVariables.light;
const { css, cx } = createEmotion({ key: "react-diff", nonce });
const content = css({
width: "auto",
overflow: "hidden",
label: "content",
});
const splitView = css({
label: "split-view",
});
const stickyHeader = css({
position: "sticky",
top: 0,
zIndex: 2,
label: "sticky-header",
});
const summary = css({
background: variables.diffViewerTitleBackground,
color: variables.diffViewerTitleColor,
padding: "0.5em 1em",
display: "flex",
alignItems: "center",
gap: "0.5em",
fontFamily: "monospace",
fontSize: 12,
fill: variables.diffViewerTitleColor,
});
const columnHeaders = css({
display: "flex",
label: "column-headers",
});
const diffContainer = css({
width: "100%",
minWidth: "1000px",
overflowX: "auto",
tableLayout: "fixed",
fontSize: 12,
background: variables.diffViewerBackground,
pre: {
margin: 0,
whiteSpace: "pre-wrap",
lineHeight: "1.6em",
width: "fit-content",
},
label: "diff-container",
borderCollapse: "collapse",
"@media (max-width: 768px)": {
minWidth: "unset",
},
});
const lineContent = css({
overflow: "hidden",
width: "100%",
});
const contentText = css({
color: variables.diffViewerColor,
whiteSpace: "pre-wrap",
fontFamily: "monospace",
lineBreak: "anywhere",
textDecoration: "none",
label: "content-text",
});
const unselectable = css({
userSelect: "none",
label: "unselectable",
});
const noWrap = css({
label: "no-wrap",
pre: {
whiteSpace: "pre",
},
[`.${contentText}`]: {
whiteSpace: "pre",
lineBreak: "auto",
},
});
const allExpandButton = css({
background: "transparent",
border: "none",
cursor: "pointer",
display: "flex",
alignItems: "center",
justifyContent: "center",
margin: 0,
label: "all-expand-button",
":hover": {
fill: variables.addedGutterColor,
},
":focus": {
outline: `1px ${variables.addedGutterColor} solid`,
},
});
const titleBlock = css({
background: variables.diffViewerTitleBackground,
padding: "0.5em",
lineHeight: "1.4em",
height: "2.4em",
overflow: "hidden",
width: "50%",
borderBottom: `1px solid ${variables.diffViewerTitleBorderColor}`,
boxSizing: "border-box",
fontSize: 12,
label: "title-block",
":only-child": {
width: "100%",
},
":last-child:not(:only-child)": {
borderLeft: `1px solid ${variables.diffViewerTitleBorderColor}`,
},
[`.${contentText}`]: {
color: variables.diffViewerTitleColor,
},
});
const lineNumber = css({
color: variables.gutterColor,
label: "line-number",
});
const diffRemoved = css({
background: variables.removedBackground,
color: variables.removedColor,
pre: {
color: variables.removedColor,
},
[`.${lineNumber}`]: {
color: variables.removedGutterColor,
},
label: "diff-removed",
});
const diffAdded = css({
background: variables.addedBackground,
color: variables.addedColor,
pre: {
color: variables.addedColor,
},
[`.${lineNumber}`]: {
color: variables.addedGutterColor,
},
label: "diff-added",
});
const diffChanged = css({
background: variables.changedBackground,
[`.${lineNumber}`]: {
color: variables.gutterColor,
},
label: "diff-changed",
});
const wordDiff = css({
display: "inline",
textDecoration: "none",
label: "word-diff",
});
const wordAdded = css({
background: variables.wordAddedBackground,
label: "word-added",
});
const wordRemoved = css({
background: variables.wordRemovedBackground,
label: "word-removed",
});
const codeFoldGutter = css({
backgroundColor: variables.codeFoldGutterBackground,
label: "code-fold-gutter",
minWidth: "50px",
width: "50px",
textAlign: "center",
fill: variables.codeFoldContentColor,
});
const codeFoldContentContainer = css({
padding: "",
});
const codeFoldExpandButton = css({
background: variables.codeFoldBackground,
cursor: "pointer",
display: "inline",
margin: 0,
border: "none",
fill: variables.codeFoldContentColor,
label: "code-fold-expand-button",
});
const codeFoldContent = css({
color: variables.codeFoldContentColor,
fontFamily: "monospace",
label: "code-fold-content",
});
const block = css({
display: "block",
width: "10px",
height: "10px",
backgroundColor: "#ddd",
borderWidth: "1px",
borderStyle: "solid",
borderColor: variables.diffViewerTitleBorderColor,
});
const blockAddition = css({
backgroundColor: variables.wordAddedBackground,
});
const blockDeletion = css({
backgroundColor: variables.wordRemovedBackground,
});
const codeFold = css({
backgroundColor: variables.codeFoldBackground,
fontSize: 12,
alignItems: "center",
userSelect: "none",
fontWeight: 700,
cursor: "pointer",
label: "code-fold",
"&:hover": {
color: variables.diffViewerColor,
fill: variables.diffViewerColor,
"& *": {
color: variables.diffViewerColor,
fill: variables.diffViewerColor,
},
},
a: {
textDecoration: "underline !important",
cursor: "pointer",
pre: {
display: "inline",
},
},
});
const emptyLine = css({
backgroundColor: variables.emptyLineBackground,
label: "empty-line",
});
const marker = css({
width: 28,
paddingLeft: 10,
paddingRight: 10,
userSelect: "none",
label: "marker",
[`&.${diffAdded}`]: {
pre: {
color: variables.addedColor,
},
},
[`&.${diffRemoved}`]: {
pre: {
color: variables.removedColor,
},
},
});
const highlightedLine = css({
background: variables.highlightBackground,
label: "highlighted-line",
[`.${wordAdded}, .${wordRemoved}`]: {
backgroundColor: "initial",
},
});
const highlightedGutter = css({
label: "highlighted-gutter",
});
const gutter = css({
userSelect: "none",
minWidth: 50,
width: "50px",
padding: "0 10px",
whiteSpace: "nowrap",
label: "gutter",
textAlign: "center",
color: variables.gutterColor,
background: variables.gutterBackground,
"&:hover": {
cursor: "pointer",
background: variables.gutterBackgroundDark,
pre: {
opacity: 1,
},
},
pre: {
opacity: 0.5,
textAlign: "center",
width: "100%",
},
[`&.${diffAdded}`]: {
background: variables.addedGutterBackground,
},
[`&.${diffRemoved}`]: {
background: variables.removedGutterBackground,
},
[`&.${highlightedGutter}`]: {
background: variables.highlightGutterBackground,
"&:hover": {
background: variables.highlightGutterBackground,
},
},
});
const emptyGutter = css({
"&:hover": {
background: variables.gutterBackground,
cursor: "initial",
},
label: "empty-gutter",
});
const line = css({
verticalAlign: "baseline",
label: "line",
textDecoration: "none",
});
const column = css({});
const defaultStyles = {
diffContainer,
diffRemoved,
diffAdded,
diffChanged,
splitView,
marker,
highlightedGutter,
highlightedLine,
gutter,
line,
lineContent,
wordDiff,
wordAdded,
summary,
block,
blockAddition,
blockDeletion,
wordRemoved,
noSelect: unselectable,
noWrap,
codeFoldGutter,
codeFoldExpandButton,
codeFoldContentContainer,
codeFold,
emptyGutter,
emptyLine,
lineNumber,
contentText,
content,
column,
codeFoldContent,
stickyHeader,
columnHeaders,
titleBlock,
allExpandButton,
};
const computerOverrideStyles = Object.keys(styles).reduce((acc, key) => ({
...acc,
...{
[key]: css(styles[key]),
},
}), {});
return Object.keys(defaultStyles).reduce((acc, key) => ({
...acc,
...{
[key]: computerOverrideStyles[key]
? cx(defaultStyles[key], computerOverrideStyles[key])
: defaultStyles[key],
},
}), {});
};