react-diff-viewer-continued 4.0.0 → 4.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.github/workflows/release.yml +23 -4
- package/.idea/react-diff-viewer-continued.iml +3 -0
- package/CHANGELOG.md +14 -0
- package/LICENSE +1 -1
- package/README.md +2 -0
- package/example.jpg +0 -0
- package/lib/cjs/src/compute-hidden-blocks.d.ts +13 -0
- package/lib/cjs/src/compute-hidden-blocks.js +39 -0
- package/lib/cjs/src/compute-lines.d.ts +55 -0
- package/lib/cjs/src/compute-lines.js +228 -0
- package/lib/cjs/src/expand.d.ts +1 -0
- package/lib/cjs/src/expand.js +8 -0
- package/lib/cjs/src/fold.d.ts +1 -0
- package/lib/cjs/src/fold.js +8 -0
- package/lib/cjs/src/index.d.ts +141 -0
- package/lib/cjs/src/index.js +373 -0
- package/lib/cjs/src/styles.d.ts +89 -0
- package/lib/cjs/src/styles.js +352 -0
- package/lib/esm/src/compute-hidden-blocks.js +35 -0
- package/lib/esm/src/compute-lines.js +202 -0
- package/lib/esm/src/expand.js +4 -0
- package/lib/esm/src/fold.js +4 -0
- package/lib/esm/src/index.js +345 -0
- package/lib/esm/src/styles.js +348 -0
- package/package.json +21 -51
- package/tsconfig.esm.json +11 -0
- package/tsconfig.json +5 -1
- package/vitest.config.ts +5 -0
- package/.eslintrc +0 -13
- package/.github/workflows/test.yml +0 -25
- package/.prettierrc +0 -4
- package/jest.config.js +0 -5
- package/webpack.config.js +0 -60
|
@@ -0,0 +1,345 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
|
+
import * as React from 'react';
|
|
3
|
+
import cn from 'classnames';
|
|
4
|
+
import { computeLineInformation, DiffMethod, DiffType, } from './compute-lines';
|
|
5
|
+
import computeStyles from './styles';
|
|
6
|
+
import { computeHiddenBlocks } from "./compute-hidden-blocks";
|
|
7
|
+
import { Expand } from "./expand";
|
|
8
|
+
import memoize from 'memoize-one';
|
|
9
|
+
import { Fold } from "./fold";
|
|
10
|
+
export var LineNumberPrefix;
|
|
11
|
+
(function (LineNumberPrefix) {
|
|
12
|
+
LineNumberPrefix["LEFT"] = "L";
|
|
13
|
+
LineNumberPrefix["RIGHT"] = "R";
|
|
14
|
+
})(LineNumberPrefix || (LineNumberPrefix = {}));
|
|
15
|
+
class DiffViewer extends React.Component {
|
|
16
|
+
styles;
|
|
17
|
+
static defaultProps = {
|
|
18
|
+
oldValue: '',
|
|
19
|
+
newValue: '',
|
|
20
|
+
splitView: true,
|
|
21
|
+
highlightLines: [],
|
|
22
|
+
disableWordDiff: false,
|
|
23
|
+
compareMethod: DiffMethod.CHARS,
|
|
24
|
+
styles: {},
|
|
25
|
+
hideLineNumbers: false,
|
|
26
|
+
extraLinesSurroundingDiff: 3,
|
|
27
|
+
showDiffOnly: true,
|
|
28
|
+
useDarkTheme: false,
|
|
29
|
+
linesOffset: 0,
|
|
30
|
+
nonce: '',
|
|
31
|
+
};
|
|
32
|
+
constructor(props) {
|
|
33
|
+
super(props);
|
|
34
|
+
this.state = {
|
|
35
|
+
expandedBlocks: [],
|
|
36
|
+
noSelect: undefined,
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Resets code block expand to the initial stage. Will be exposed to the parent component via
|
|
41
|
+
* refs.
|
|
42
|
+
*/
|
|
43
|
+
resetCodeBlocks = () => {
|
|
44
|
+
if (this.state.expandedBlocks.length > 0) {
|
|
45
|
+
this.setState({
|
|
46
|
+
expandedBlocks: [],
|
|
47
|
+
});
|
|
48
|
+
return true;
|
|
49
|
+
}
|
|
50
|
+
return false;
|
|
51
|
+
};
|
|
52
|
+
/**
|
|
53
|
+
* Pushes the target expanded code block to the state. During the re-render,
|
|
54
|
+
* this value is used to expand/fold unmodified code.
|
|
55
|
+
*/
|
|
56
|
+
onBlockExpand = (id) => {
|
|
57
|
+
const prevState = this.state.expandedBlocks.slice();
|
|
58
|
+
prevState.push(id);
|
|
59
|
+
this.setState({
|
|
60
|
+
expandedBlocks: prevState,
|
|
61
|
+
});
|
|
62
|
+
};
|
|
63
|
+
/**
|
|
64
|
+
* Computes final styles for the diff viewer. It combines the default styles with the user
|
|
65
|
+
* supplied overrides. The computed styles are cached with performance in mind.
|
|
66
|
+
*
|
|
67
|
+
* @param styles User supplied style overrides.
|
|
68
|
+
*/
|
|
69
|
+
computeStyles = memoize(computeStyles);
|
|
70
|
+
/**
|
|
71
|
+
* Returns a function with clicked line number in the closure. Returns an no-op function when no
|
|
72
|
+
* onLineNumberClick handler is supplied.
|
|
73
|
+
*
|
|
74
|
+
* @param id Line id of a line.
|
|
75
|
+
*/
|
|
76
|
+
onLineNumberClickProxy = (id) => {
|
|
77
|
+
if (this.props.onLineNumberClick) {
|
|
78
|
+
return (e) => this.props.onLineNumberClick(id, e);
|
|
79
|
+
}
|
|
80
|
+
return () => { };
|
|
81
|
+
};
|
|
82
|
+
/**
|
|
83
|
+
* Maps over the word diff and constructs the required React elements to show word diff.
|
|
84
|
+
*
|
|
85
|
+
* @param diffArray Word diff information derived from line information.
|
|
86
|
+
* @param renderer Optional renderer to format diff words. Useful for syntax highlighting.
|
|
87
|
+
*/
|
|
88
|
+
renderWordDiff = (diffArray, renderer) => {
|
|
89
|
+
return diffArray.map((wordDiff, i) => {
|
|
90
|
+
const content = renderer ? renderer(wordDiff.value) : wordDiff.value;
|
|
91
|
+
if (typeof content !== 'string')
|
|
92
|
+
return;
|
|
93
|
+
return wordDiff.type === DiffType.ADDED ?
|
|
94
|
+
_jsx("ins", { className: cn(this.styles.wordDiff, {
|
|
95
|
+
[this.styles.wordAdded]: wordDiff.type === DiffType.ADDED,
|
|
96
|
+
}), children: content }, i)
|
|
97
|
+
: wordDiff.type === DiffType.REMOVED ? _jsx("del", { className: cn(this.styles.wordDiff, {
|
|
98
|
+
[this.styles.wordRemoved]: wordDiff.type === DiffType.REMOVED,
|
|
99
|
+
}), children: content }, i) : _jsx("span", { className: cn(this.styles.wordDiff), children: content }, i);
|
|
100
|
+
});
|
|
101
|
+
};
|
|
102
|
+
/**
|
|
103
|
+
* Maps over the line diff and constructs the required react elements to show line diff. It calls
|
|
104
|
+
* renderWordDiff when encountering word diff. This takes care of both inline and split view line
|
|
105
|
+
* renders.
|
|
106
|
+
*
|
|
107
|
+
* @param lineNumber Line number of the current line.
|
|
108
|
+
* @param type Type of diff of the current line.
|
|
109
|
+
* @param prefix Unique id to prefix with the line numbers.
|
|
110
|
+
* @param value Content of the line. It can be a string or a word diff array.
|
|
111
|
+
* @param additionalLineNumber Additional line number to be shown. Useful for rendering inline
|
|
112
|
+
* diff view. Right line number will be passed as additionalLineNumber.
|
|
113
|
+
* @param additionalPrefix Similar to prefix but for additional line number.
|
|
114
|
+
*/
|
|
115
|
+
renderLine = (lineNumber, type, prefix, value, additionalLineNumber, additionalPrefix) => {
|
|
116
|
+
const lineNumberTemplate = `${prefix}-${lineNumber}`;
|
|
117
|
+
const additionalLineNumberTemplate = `${additionalPrefix}-${additionalLineNumber}`;
|
|
118
|
+
const highlightLine = this.props.highlightLines.includes(lineNumberTemplate) ||
|
|
119
|
+
this.props.highlightLines.includes(additionalLineNumberTemplate);
|
|
120
|
+
const added = type === DiffType.ADDED;
|
|
121
|
+
const removed = type === DiffType.REMOVED;
|
|
122
|
+
const changed = type === DiffType.CHANGED;
|
|
123
|
+
let content;
|
|
124
|
+
const hasWordDiff = Array.isArray(value);
|
|
125
|
+
if (hasWordDiff) {
|
|
126
|
+
content = this.renderWordDiff(value, this.props.renderContent);
|
|
127
|
+
}
|
|
128
|
+
else if (this.props.renderContent) {
|
|
129
|
+
content = this.props.renderContent(value);
|
|
130
|
+
}
|
|
131
|
+
else {
|
|
132
|
+
content = value;
|
|
133
|
+
}
|
|
134
|
+
let ElementType = 'div';
|
|
135
|
+
if (added && !hasWordDiff) {
|
|
136
|
+
ElementType = 'ins';
|
|
137
|
+
}
|
|
138
|
+
else if (removed && !hasWordDiff) {
|
|
139
|
+
ElementType = 'del';
|
|
140
|
+
}
|
|
141
|
+
return (_jsxs(_Fragment, { children: [!this.props.hideLineNumbers && (_jsx("td", { onClick: lineNumber && this.onLineNumberClickProxy(lineNumberTemplate), className: cn(this.styles.gutter, {
|
|
142
|
+
[this.styles.emptyGutter]: !lineNumber,
|
|
143
|
+
[this.styles.diffAdded]: added,
|
|
144
|
+
[this.styles.diffRemoved]: removed,
|
|
145
|
+
[this.styles.diffChanged]: changed,
|
|
146
|
+
[this.styles.highlightedGutter]: highlightLine,
|
|
147
|
+
}), children: _jsx("pre", { className: this.styles.lineNumber, children: lineNumber }) })), !this.props.splitView && !this.props.hideLineNumbers && (_jsx("td", { onClick: additionalLineNumber &&
|
|
148
|
+
this.onLineNumberClickProxy(additionalLineNumberTemplate), className: cn(this.styles.gutter, {
|
|
149
|
+
[this.styles.emptyGutter]: !additionalLineNumber,
|
|
150
|
+
[this.styles.diffAdded]: added,
|
|
151
|
+
[this.styles.diffRemoved]: removed,
|
|
152
|
+
[this.styles.diffChanged]: changed,
|
|
153
|
+
[this.styles.highlightedGutter]: highlightLine,
|
|
154
|
+
}), children: _jsx("pre", { className: this.styles.lineNumber, children: additionalLineNumber }) })), this.props.renderGutter
|
|
155
|
+
? this.props.renderGutter({
|
|
156
|
+
lineNumber,
|
|
157
|
+
type,
|
|
158
|
+
prefix,
|
|
159
|
+
value,
|
|
160
|
+
additionalLineNumber,
|
|
161
|
+
additionalPrefix,
|
|
162
|
+
styles: this.styles,
|
|
163
|
+
})
|
|
164
|
+
: null, _jsx("td", { className: cn(this.styles.marker, {
|
|
165
|
+
[this.styles.emptyLine]: !content,
|
|
166
|
+
[this.styles.diffAdded]: added,
|
|
167
|
+
[this.styles.diffRemoved]: removed,
|
|
168
|
+
[this.styles.diffChanged]: changed,
|
|
169
|
+
[this.styles.highlightedLine]: highlightLine,
|
|
170
|
+
}), children: _jsxs("pre", { children: [added && '+', removed && '-'] }) }), _jsx("td", { className: cn(this.styles.content, {
|
|
171
|
+
[this.styles.emptyLine]: !content,
|
|
172
|
+
[this.styles.diffAdded]: added,
|
|
173
|
+
[this.styles.diffRemoved]: removed,
|
|
174
|
+
[this.styles.diffChanged]: changed,
|
|
175
|
+
[this.styles.highlightedLine]: highlightLine,
|
|
176
|
+
left: prefix === LineNumberPrefix.LEFT,
|
|
177
|
+
right: prefix === LineNumberPrefix.RIGHT,
|
|
178
|
+
}), onMouseDown: () => {
|
|
179
|
+
const elements = document.getElementsByClassName(prefix === LineNumberPrefix.LEFT ? 'right' : 'left');
|
|
180
|
+
for (let i = 0; i < elements.length; i++) {
|
|
181
|
+
const element = elements.item(i);
|
|
182
|
+
element.classList.add(this.styles.noSelect);
|
|
183
|
+
}
|
|
184
|
+
}, title: added && !hasWordDiff ? "Added line" : removed && !hasWordDiff ? "Removed line" : undefined, children: _jsx(ElementType, { className: this.styles.contentText, children: content }) })] }));
|
|
185
|
+
};
|
|
186
|
+
/**
|
|
187
|
+
* Generates lines for split view.
|
|
188
|
+
*
|
|
189
|
+
* @param obj Line diff information.
|
|
190
|
+
* @param obj.left Life diff information for the left pane of the split view.
|
|
191
|
+
* @param obj.right Life diff information for the right pane of the split view.
|
|
192
|
+
* @param index React key for the lines.
|
|
193
|
+
*/
|
|
194
|
+
renderSplitView = ({ left, right }, index) => {
|
|
195
|
+
return (_jsxs("tr", { className: this.styles.line, children: [this.renderLine(left.lineNumber, left.type, LineNumberPrefix.LEFT, left.value), this.renderLine(right.lineNumber, right.type, LineNumberPrefix.RIGHT, right.value)] }, index));
|
|
196
|
+
};
|
|
197
|
+
/**
|
|
198
|
+
* Generates lines for inline view.
|
|
199
|
+
*
|
|
200
|
+
* @param obj Line diff information.
|
|
201
|
+
* @param obj.left Life diff information for the added section of the inline view.
|
|
202
|
+
* @param obj.right Life diff information for the removed section of the inline view.
|
|
203
|
+
* @param index React key for the lines.
|
|
204
|
+
*/
|
|
205
|
+
renderInlineView = ({ left, right }, index) => {
|
|
206
|
+
let content;
|
|
207
|
+
if (left.type === DiffType.REMOVED && right.type === DiffType.ADDED) {
|
|
208
|
+
return (_jsxs(React.Fragment, { children: [_jsx("tr", { className: this.styles.line, children: this.renderLine(left.lineNumber, left.type, LineNumberPrefix.LEFT, left.value, null) }), _jsx("tr", { className: this.styles.line, children: this.renderLine(null, right.type, LineNumberPrefix.RIGHT, right.value, right.lineNumber) })] }, index));
|
|
209
|
+
}
|
|
210
|
+
if (left.type === DiffType.REMOVED) {
|
|
211
|
+
content = this.renderLine(left.lineNumber, left.type, LineNumberPrefix.LEFT, left.value, null);
|
|
212
|
+
}
|
|
213
|
+
if (left.type === DiffType.DEFAULT) {
|
|
214
|
+
content = this.renderLine(left.lineNumber, left.type, LineNumberPrefix.LEFT, left.value, right.lineNumber, LineNumberPrefix.RIGHT);
|
|
215
|
+
}
|
|
216
|
+
if (right.type === DiffType.ADDED) {
|
|
217
|
+
content = this.renderLine(null, right.type, LineNumberPrefix.RIGHT, right.value, right.lineNumber);
|
|
218
|
+
}
|
|
219
|
+
return (_jsx("tr", { className: this.styles.line, children: content }, index));
|
|
220
|
+
};
|
|
221
|
+
/**
|
|
222
|
+
* Returns a function with clicked block number in the closure.
|
|
223
|
+
*
|
|
224
|
+
* @param id Cold fold block id.
|
|
225
|
+
*/
|
|
226
|
+
onBlockClickProxy = (id) => () => this.onBlockExpand(id);
|
|
227
|
+
/**
|
|
228
|
+
* Generates cold fold block. It also uses the custom message renderer when available to show
|
|
229
|
+
* cold fold messages.
|
|
230
|
+
*
|
|
231
|
+
* @param num Number of skipped lines between two blocks.
|
|
232
|
+
* @param blockNumber Code fold block id.
|
|
233
|
+
* @param leftBlockLineNumber First left line number after the current code fold block.
|
|
234
|
+
* @param rightBlockLineNumber First right line number after the current code fold block.
|
|
235
|
+
*/
|
|
236
|
+
renderSkippedLineIndicator = (num, blockNumber, leftBlockLineNumber, rightBlockLineNumber) => {
|
|
237
|
+
const { hideLineNumbers, splitView } = this.props;
|
|
238
|
+
const message = this.props.codeFoldMessageRenderer ? (this.props.codeFoldMessageRenderer(num, leftBlockLineNumber, rightBlockLineNumber)) : (_jsxs("pre", { className: this.styles.codeFoldContent, children: ["Expand ", num, " lines ..."] }));
|
|
239
|
+
const content = (_jsx("td", { className: this.styles.codeFoldContentContainer, children: _jsx("a", { onClick: this.onBlockClickProxy(blockNumber), tabIndex: 0, children: message }) }));
|
|
240
|
+
const isUnifiedViewWithoutLineNumbers = !splitView && !hideLineNumbers;
|
|
241
|
+
return (_jsxs("tr", { className: this.styles.codeFold, children: [!hideLineNumbers && _jsx("td", { className: this.styles.codeFoldGutter }), this.props.renderGutter ? (_jsx("td", { className: this.styles.codeFoldGutter })) : null, _jsx("td", { className: cn({
|
|
242
|
+
[this.styles.codeFoldGutter]: isUnifiedViewWithoutLineNumbers,
|
|
243
|
+
}) }), isUnifiedViewWithoutLineNumbers ? (_jsxs(React.Fragment, { children: [_jsx("td", {}), content] })) : (_jsxs(React.Fragment, { children: [content, this.props.renderGutter ? _jsx("td", {}) : null, _jsx("td", {}), _jsx("td", {}), !hideLineNumbers ? _jsx("td", {}) : null] }))] }, `${leftBlockLineNumber}-${rightBlockLineNumber}`));
|
|
244
|
+
};
|
|
245
|
+
/**
|
|
246
|
+
* Generates the entire diff view.
|
|
247
|
+
*/
|
|
248
|
+
renderDiff = () => {
|
|
249
|
+
const { oldValue, newValue, splitView, disableWordDiff, compareMethod, linesOffset, } = this.props;
|
|
250
|
+
const { lineInformation, diffLines } = computeLineInformation(oldValue, newValue, disableWordDiff, compareMethod, linesOffset, this.props.alwaysShowLines);
|
|
251
|
+
const extraLines = this.props.extraLinesSurroundingDiff < 0
|
|
252
|
+
? 0
|
|
253
|
+
: Math.round(this.props.extraLinesSurroundingDiff);
|
|
254
|
+
const { lineBlocks, blocks } = computeHiddenBlocks(lineInformation, diffLines, extraLines);
|
|
255
|
+
const diffNodes = lineInformation.map((line, lineIndex) => {
|
|
256
|
+
if (this.props.showDiffOnly) {
|
|
257
|
+
const blockIndex = lineBlocks[lineIndex];
|
|
258
|
+
if (blockIndex !== undefined) {
|
|
259
|
+
const lastLineOfBlock = blocks[blockIndex].endLine === lineIndex;
|
|
260
|
+
if (!this.state.expandedBlocks.includes(blockIndex) && lastLineOfBlock) {
|
|
261
|
+
return (_jsx(React.Fragment, { children: this.renderSkippedLineIndicator(blocks[blockIndex].lines, blockIndex, line.left.lineNumber, line.right.lineNumber) }, lineIndex));
|
|
262
|
+
}
|
|
263
|
+
else if (!this.state.expandedBlocks.includes(blockIndex)) {
|
|
264
|
+
return null;
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
return splitView
|
|
269
|
+
? this.renderSplitView(line, lineIndex)
|
|
270
|
+
: this.renderInlineView(line, lineIndex);
|
|
271
|
+
});
|
|
272
|
+
return {
|
|
273
|
+
diffNodes,
|
|
274
|
+
blocks,
|
|
275
|
+
lineInformation
|
|
276
|
+
};
|
|
277
|
+
};
|
|
278
|
+
render = () => {
|
|
279
|
+
const { oldValue, newValue, useDarkTheme, leftTitle, rightTitle, splitView, hideLineNumbers, nonce, } = this.props;
|
|
280
|
+
if (this.props.compareMethod !== DiffMethod.JSON) {
|
|
281
|
+
if (typeof oldValue !== 'string' || typeof newValue !== 'string') {
|
|
282
|
+
throw Error('"oldValue" and "newValue" should be strings');
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
this.styles = this.computeStyles(this.props.styles, useDarkTheme, nonce);
|
|
286
|
+
const nodes = this.renderDiff();
|
|
287
|
+
let colSpanOnSplitView = 3;
|
|
288
|
+
let colSpanOnInlineView = 4;
|
|
289
|
+
if (hideLineNumbers) {
|
|
290
|
+
colSpanOnSplitView -= 1;
|
|
291
|
+
colSpanOnInlineView -= 1;
|
|
292
|
+
}
|
|
293
|
+
if (this.props.renderGutter) {
|
|
294
|
+
colSpanOnSplitView += 1;
|
|
295
|
+
colSpanOnInlineView += 1;
|
|
296
|
+
}
|
|
297
|
+
let deletions = 0, additions = 0;
|
|
298
|
+
nodes.lineInformation.forEach((l) => {
|
|
299
|
+
if (l.left.type === DiffType.ADDED) {
|
|
300
|
+
additions++;
|
|
301
|
+
}
|
|
302
|
+
if (l.right.type === DiffType.ADDED) {
|
|
303
|
+
additions++;
|
|
304
|
+
}
|
|
305
|
+
if (l.left.type === DiffType.REMOVED) {
|
|
306
|
+
deletions++;
|
|
307
|
+
}
|
|
308
|
+
if (l.right.type === DiffType.REMOVED) {
|
|
309
|
+
deletions++;
|
|
310
|
+
}
|
|
311
|
+
});
|
|
312
|
+
const totalChanges = deletions + additions;
|
|
313
|
+
const percentageAddition = Math.round((additions / totalChanges) * 100);
|
|
314
|
+
const blocks = [];
|
|
315
|
+
for (let i = 0; i < 5; i++) {
|
|
316
|
+
if (percentageAddition > i * 20) {
|
|
317
|
+
blocks.push(_jsx("span", { className: cn(this.styles.block, this.styles.blockAddition) }, i));
|
|
318
|
+
}
|
|
319
|
+
else {
|
|
320
|
+
blocks.push(_jsx("span", { className: cn(this.styles.block, this.styles.blockDeletion) }, i));
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
const allExpanded = this.state.expandedBlocks.length === nodes.blocks.length;
|
|
324
|
+
return (_jsxs("div", { children: [_jsxs("div", { className: this.styles.summary, role: 'banner', children: [_jsx("a", { style: { cursor: 'pointer' }, onClick: () => {
|
|
325
|
+
this.setState({
|
|
326
|
+
expandedBlocks: allExpanded ? [] : nodes.blocks.map(b => b.index)
|
|
327
|
+
});
|
|
328
|
+
}, children: allExpanded ? _jsx(Fold, {}) : _jsx(Expand, {}) }), " ", totalChanges, _jsx("div", { style: { display: 'flex', gap: '1px' }, children: blocks }), this.props.summary ? _jsx("span", { children: this.props.summary }) : null] }), _jsx("table", { className: cn(this.styles.diffContainer, {
|
|
329
|
+
[this.styles.splitView]: splitView,
|
|
330
|
+
}), onMouseUp: () => {
|
|
331
|
+
const elements = document.getElementsByClassName('right');
|
|
332
|
+
for (let i = 0; i < elements.length; i++) {
|
|
333
|
+
const element = elements.item(i);
|
|
334
|
+
element.classList.remove(this.styles.noSelect);
|
|
335
|
+
}
|
|
336
|
+
const elementsLeft = document.getElementsByClassName('left');
|
|
337
|
+
for (let i = 0; i < elementsLeft.length; i++) {
|
|
338
|
+
const element = elementsLeft.item(i);
|
|
339
|
+
element.classList.remove(this.styles.noSelect);
|
|
340
|
+
}
|
|
341
|
+
}, children: _jsxs("tbody", { children: [_jsxs("tr", { children: [!this.props.hideLineNumbers ? _jsx("td", { width: '50px' }) : null, !splitView && !this.props.hideLineNumbers ? _jsx("td", { width: '50px' }) : null, this.props.renderGutter ? _jsx("td", { width: '50px' }) : null, _jsx("td", { width: '28px' }), _jsx("td", { width: '100%' }), splitView ? _jsxs(_Fragment, { children: [!this.props.hideLineNumbers ? _jsx("td", { width: '50px' }) : null, this.props.renderGutter ? _jsx("td", { width: '50px' }) : null, _jsx("td", { width: '28px' }), _jsx("td", { width: '100%' })] }) : null] }), leftTitle || rightTitle ? _jsxs("tr", { children: [_jsx("td", { colSpan: (splitView ? colSpanOnSplitView : colSpanOnInlineView), className: cn(this.styles.titleBlock, this.styles.column), role: 'columnheader', children: leftTitle ? _jsx("pre", { className: this.styles.contentText, children: leftTitle }) : null }), splitView ? _jsx("td", { colSpan: colSpanOnSplitView, className: cn(this.styles.titleBlock, this.styles.column), role: 'columnheader', children: rightTitle ? _jsx("pre", { className: this.styles.contentText, children: rightTitle }) : null }) : null] }) : null, nodes.diffNodes] }) })] }));
|
|
342
|
+
};
|
|
343
|
+
}
|
|
344
|
+
export default DiffViewer;
|
|
345
|
+
export { DiffMethod };
|
|
@@ -0,0 +1,348 @@
|
|
|
1
|
+
import createEmotion from '@emotion/css/create-instance';
|
|
2
|
+
// eslint-disable-next-line import/no-anonymous-default-export
|
|
3
|
+
export default (styleOverride, useDarkTheme = false, nonce = '') => {
|
|
4
|
+
const { variables: overrideVariables = {}, ...styles } = styleOverride;
|
|
5
|
+
const themeVariables = {
|
|
6
|
+
light: {
|
|
7
|
+
...{
|
|
8
|
+
diffViewerBackground: '#fff',
|
|
9
|
+
diffViewerColor: '#212529',
|
|
10
|
+
addedBackground: '#e6ffed',
|
|
11
|
+
addedColor: '#24292e',
|
|
12
|
+
removedBackground: '#ffeef0',
|
|
13
|
+
removedColor: '#24292e',
|
|
14
|
+
changedBackground: '#fffbdd',
|
|
15
|
+
wordAddedBackground: '#acf2bd',
|
|
16
|
+
wordRemovedBackground: '#fdb8c0',
|
|
17
|
+
addedGutterBackground: '#cdffd8',
|
|
18
|
+
removedGutterBackground: '#ffdce0',
|
|
19
|
+
gutterBackground: '#f7f7f7',
|
|
20
|
+
gutterBackgroundDark: '#f3f1f1',
|
|
21
|
+
highlightBackground: '#fffbdd',
|
|
22
|
+
highlightGutterBackground: '#fff5b1',
|
|
23
|
+
codeFoldGutterBackground: '#dbedff',
|
|
24
|
+
codeFoldBackground: '#f1f8ff',
|
|
25
|
+
emptyLineBackground: '#fafbfc',
|
|
26
|
+
gutterColor: '#212529',
|
|
27
|
+
addedGutterColor: '#212529',
|
|
28
|
+
removedGutterColor: '#212529',
|
|
29
|
+
codeFoldContentColor: '#212529',
|
|
30
|
+
diffViewerTitleBackground: '#fafbfc',
|
|
31
|
+
diffViewerTitleColor: '#212529',
|
|
32
|
+
diffViewerTitleBorderColor: '#eee',
|
|
33
|
+
},
|
|
34
|
+
...(overrideVariables.light || {}),
|
|
35
|
+
},
|
|
36
|
+
dark: {
|
|
37
|
+
...{
|
|
38
|
+
diffViewerBackground: '#2e303c',
|
|
39
|
+
diffViewerColor: '#FFF',
|
|
40
|
+
addedBackground: '#044B53',
|
|
41
|
+
addedColor: 'white',
|
|
42
|
+
removedBackground: '#632F34',
|
|
43
|
+
removedColor: 'white',
|
|
44
|
+
changedBackground: '#3e302c',
|
|
45
|
+
wordAddedBackground: '#055d67',
|
|
46
|
+
wordRemovedBackground: '#7d383f',
|
|
47
|
+
addedGutterBackground: '#034148',
|
|
48
|
+
removedGutterBackground: '#632b30',
|
|
49
|
+
gutterBackground: '#2c2f3a',
|
|
50
|
+
gutterBackgroundDark: '#262933',
|
|
51
|
+
highlightBackground: '#2a3967',
|
|
52
|
+
highlightGutterBackground: '#2d4077',
|
|
53
|
+
codeFoldGutterBackground: '#262831',
|
|
54
|
+
codeFoldBackground: '#262831',
|
|
55
|
+
emptyLineBackground: '#363946',
|
|
56
|
+
gutterColor: '#666c87',
|
|
57
|
+
addedGutterColor: '#8c8c8c',
|
|
58
|
+
removedGutterColor: '#8c8c8c',
|
|
59
|
+
codeFoldContentColor: '#656a8b',
|
|
60
|
+
diffViewerTitleBackground: '#2f323e',
|
|
61
|
+
diffViewerTitleColor: '#757a9b',
|
|
62
|
+
diffViewerTitleBorderColor: '#353846',
|
|
63
|
+
},
|
|
64
|
+
...(overrideVariables.dark || {}),
|
|
65
|
+
},
|
|
66
|
+
};
|
|
67
|
+
const variables = useDarkTheme ? themeVariables.dark : themeVariables.light;
|
|
68
|
+
const { css, cx } = createEmotion({ key: 'react-diff', nonce });
|
|
69
|
+
const content = css({
|
|
70
|
+
width: 'auto',
|
|
71
|
+
label: 'content',
|
|
72
|
+
});
|
|
73
|
+
const splitView = css({
|
|
74
|
+
label: 'split-view',
|
|
75
|
+
});
|
|
76
|
+
const summary = css({
|
|
77
|
+
background: variables.diffViewerTitleBackground,
|
|
78
|
+
color: variables.diffViewerTitleColor,
|
|
79
|
+
padding: '0.5em 1em',
|
|
80
|
+
display: 'flex',
|
|
81
|
+
alignItems: 'center',
|
|
82
|
+
gap: '0.5em',
|
|
83
|
+
fontFamily: "monospace",
|
|
84
|
+
fill: variables.diffViewerTitleColor,
|
|
85
|
+
});
|
|
86
|
+
const diffContainer = css({
|
|
87
|
+
width: '100%',
|
|
88
|
+
minWidth: '1000px',
|
|
89
|
+
overflowX: 'auto',
|
|
90
|
+
tableLayout: 'fixed',
|
|
91
|
+
background: variables.diffViewerBackground,
|
|
92
|
+
pre: {
|
|
93
|
+
margin: 0,
|
|
94
|
+
whiteSpace: 'pre-wrap',
|
|
95
|
+
lineHeight: '1.6em',
|
|
96
|
+
width: 'fit-content'
|
|
97
|
+
},
|
|
98
|
+
label: 'diff-container',
|
|
99
|
+
borderCollapse: 'collapse',
|
|
100
|
+
});
|
|
101
|
+
const lineContent = css({
|
|
102
|
+
overflow: 'hidden',
|
|
103
|
+
width: '100%'
|
|
104
|
+
});
|
|
105
|
+
const contentText = css({
|
|
106
|
+
color: variables.diffViewerColor,
|
|
107
|
+
whiteSpace: 'pre-wrap',
|
|
108
|
+
fontFamily: 'monospace',
|
|
109
|
+
textDecoration: 'none',
|
|
110
|
+
label: 'content-text',
|
|
111
|
+
});
|
|
112
|
+
const unselectable = css({
|
|
113
|
+
userSelect: 'none',
|
|
114
|
+
label: 'unselectable',
|
|
115
|
+
});
|
|
116
|
+
const titleBlock = css({
|
|
117
|
+
background: variables.diffViewerTitleBackground,
|
|
118
|
+
padding: '0.5em',
|
|
119
|
+
lineHeight: '1.4em',
|
|
120
|
+
height: "2.4em",
|
|
121
|
+
overflow: 'hidden',
|
|
122
|
+
width: '50%',
|
|
123
|
+
borderBottom: `1px solid ${variables.diffViewerTitleBorderColor}`,
|
|
124
|
+
label: 'title-block',
|
|
125
|
+
':last-child': {
|
|
126
|
+
borderLeft: `1px solid ${variables.diffViewerTitleBorderColor}`,
|
|
127
|
+
},
|
|
128
|
+
[`.${contentText}`]: {
|
|
129
|
+
color: variables.diffViewerTitleColor,
|
|
130
|
+
},
|
|
131
|
+
});
|
|
132
|
+
const lineNumber = css({
|
|
133
|
+
color: variables.gutterColor,
|
|
134
|
+
label: 'line-number',
|
|
135
|
+
});
|
|
136
|
+
const diffRemoved = css({
|
|
137
|
+
background: variables.removedBackground,
|
|
138
|
+
color: variables.removedColor,
|
|
139
|
+
pre: {
|
|
140
|
+
color: variables.removedColor,
|
|
141
|
+
},
|
|
142
|
+
[`.${lineNumber}`]: {
|
|
143
|
+
color: variables.removedGutterColor,
|
|
144
|
+
},
|
|
145
|
+
label: 'diff-removed',
|
|
146
|
+
});
|
|
147
|
+
const diffAdded = css({
|
|
148
|
+
background: variables.addedBackground,
|
|
149
|
+
color: variables.addedColor,
|
|
150
|
+
pre: {
|
|
151
|
+
color: variables.addedColor,
|
|
152
|
+
},
|
|
153
|
+
[`.${lineNumber}`]: {
|
|
154
|
+
color: variables.addedGutterColor,
|
|
155
|
+
},
|
|
156
|
+
label: 'diff-added',
|
|
157
|
+
});
|
|
158
|
+
const diffChanged = css({
|
|
159
|
+
background: variables.changedBackground,
|
|
160
|
+
[`.${lineNumber}`]: {
|
|
161
|
+
color: variables.gutterColor,
|
|
162
|
+
},
|
|
163
|
+
label: 'diff-changed',
|
|
164
|
+
});
|
|
165
|
+
const wordDiff = css({
|
|
166
|
+
padding: 2,
|
|
167
|
+
display: 'inline-flex',
|
|
168
|
+
borderRadius: 4,
|
|
169
|
+
wordBreak: 'break-all',
|
|
170
|
+
label: 'word-diff',
|
|
171
|
+
});
|
|
172
|
+
const wordAdded = css({
|
|
173
|
+
background: variables.wordAddedBackground,
|
|
174
|
+
textDecoration: 'none',
|
|
175
|
+
label: 'word-added',
|
|
176
|
+
});
|
|
177
|
+
const wordRemoved = css({
|
|
178
|
+
background: variables.wordRemovedBackground,
|
|
179
|
+
textDecoration: 'none',
|
|
180
|
+
label: 'word-removed',
|
|
181
|
+
});
|
|
182
|
+
const codeFoldGutter = css({
|
|
183
|
+
backgroundColor: variables.codeFoldGutterBackground,
|
|
184
|
+
label: 'code-fold-gutter',
|
|
185
|
+
minWidth: '50px',
|
|
186
|
+
width: '50px'
|
|
187
|
+
});
|
|
188
|
+
const codeFoldContentContainer = css({
|
|
189
|
+
padding: '8px 16px'
|
|
190
|
+
});
|
|
191
|
+
const codeFoldContent = css({
|
|
192
|
+
color: variables.codeFoldContentColor,
|
|
193
|
+
label: 'code-fold-content',
|
|
194
|
+
});
|
|
195
|
+
const block = css({
|
|
196
|
+
display: 'block',
|
|
197
|
+
width: '10px',
|
|
198
|
+
height: '10px',
|
|
199
|
+
backgroundColor: '#ddd',
|
|
200
|
+
borderWidth: '1px',
|
|
201
|
+
borderStyle: 'solid',
|
|
202
|
+
borderColor: variables.diffViewerTitleBorderColor
|
|
203
|
+
});
|
|
204
|
+
const blockAddition = css({
|
|
205
|
+
backgroundColor: variables.wordAddedBackground
|
|
206
|
+
});
|
|
207
|
+
const blockDeletion = css({
|
|
208
|
+
backgroundColor: variables.wordRemovedBackground
|
|
209
|
+
});
|
|
210
|
+
const codeFold = css({
|
|
211
|
+
backgroundColor: variables.codeFoldBackground,
|
|
212
|
+
height: 40,
|
|
213
|
+
fontSize: 14,
|
|
214
|
+
alignItems: 'center',
|
|
215
|
+
userSelect: 'none',
|
|
216
|
+
fontWeight: 700,
|
|
217
|
+
label: 'code-fold',
|
|
218
|
+
a: {
|
|
219
|
+
textDecoration: 'underline !important',
|
|
220
|
+
cursor: 'pointer',
|
|
221
|
+
pre: {
|
|
222
|
+
display: 'inline',
|
|
223
|
+
},
|
|
224
|
+
},
|
|
225
|
+
});
|
|
226
|
+
const emptyLine = css({
|
|
227
|
+
backgroundColor: variables.emptyLineBackground,
|
|
228
|
+
label: 'empty-line',
|
|
229
|
+
});
|
|
230
|
+
const marker = css({
|
|
231
|
+
width: 28,
|
|
232
|
+
paddingLeft: 10,
|
|
233
|
+
paddingRight: 10,
|
|
234
|
+
userSelect: 'none',
|
|
235
|
+
label: 'marker',
|
|
236
|
+
[`&.${diffAdded}`]: {
|
|
237
|
+
pre: {
|
|
238
|
+
color: variables.addedColor,
|
|
239
|
+
},
|
|
240
|
+
},
|
|
241
|
+
[`&.${diffRemoved}`]: {
|
|
242
|
+
pre: {
|
|
243
|
+
color: variables.removedColor,
|
|
244
|
+
},
|
|
245
|
+
},
|
|
246
|
+
});
|
|
247
|
+
const highlightedLine = css({
|
|
248
|
+
background: variables.highlightBackground,
|
|
249
|
+
label: 'highlighted-line',
|
|
250
|
+
[`.${wordAdded}, .${wordRemoved}`]: {
|
|
251
|
+
backgroundColor: 'initial',
|
|
252
|
+
},
|
|
253
|
+
});
|
|
254
|
+
const highlightedGutter = css({
|
|
255
|
+
label: 'highlighted-gutter',
|
|
256
|
+
});
|
|
257
|
+
const gutter = css({
|
|
258
|
+
userSelect: 'none',
|
|
259
|
+
minWidth: 50,
|
|
260
|
+
width: '50px',
|
|
261
|
+
padding: '0 10px',
|
|
262
|
+
whiteSpace: 'nowrap',
|
|
263
|
+
label: 'gutter',
|
|
264
|
+
textAlign: 'right',
|
|
265
|
+
background: variables.gutterBackground,
|
|
266
|
+
'&:hover': {
|
|
267
|
+
cursor: 'pointer',
|
|
268
|
+
background: variables.gutterBackgroundDark,
|
|
269
|
+
pre: {
|
|
270
|
+
opacity: 1,
|
|
271
|
+
},
|
|
272
|
+
},
|
|
273
|
+
pre: {
|
|
274
|
+
opacity: 0.5,
|
|
275
|
+
},
|
|
276
|
+
[`&.${diffAdded}`]: {
|
|
277
|
+
background: variables.addedGutterBackground,
|
|
278
|
+
},
|
|
279
|
+
[`&.${diffRemoved}`]: {
|
|
280
|
+
background: variables.removedGutterBackground,
|
|
281
|
+
},
|
|
282
|
+
[`&.${highlightedGutter}`]: {
|
|
283
|
+
background: variables.highlightGutterBackground,
|
|
284
|
+
'&:hover': {
|
|
285
|
+
background: variables.highlightGutterBackground,
|
|
286
|
+
},
|
|
287
|
+
},
|
|
288
|
+
});
|
|
289
|
+
const emptyGutter = css({
|
|
290
|
+
'&:hover': {
|
|
291
|
+
background: variables.gutterBackground,
|
|
292
|
+
cursor: 'initial',
|
|
293
|
+
},
|
|
294
|
+
label: 'empty-gutter',
|
|
295
|
+
});
|
|
296
|
+
const line = css({
|
|
297
|
+
verticalAlign: 'baseline',
|
|
298
|
+
label: 'line',
|
|
299
|
+
textDecoration: 'none'
|
|
300
|
+
});
|
|
301
|
+
const column = css({});
|
|
302
|
+
const defaultStyles = {
|
|
303
|
+
diffContainer,
|
|
304
|
+
diffRemoved,
|
|
305
|
+
diffAdded,
|
|
306
|
+
diffChanged,
|
|
307
|
+
splitView,
|
|
308
|
+
marker,
|
|
309
|
+
highlightedGutter,
|
|
310
|
+
highlightedLine,
|
|
311
|
+
gutter,
|
|
312
|
+
line,
|
|
313
|
+
lineContent,
|
|
314
|
+
wordDiff,
|
|
315
|
+
wordAdded,
|
|
316
|
+
summary,
|
|
317
|
+
block,
|
|
318
|
+
blockAddition,
|
|
319
|
+
blockDeletion,
|
|
320
|
+
wordRemoved,
|
|
321
|
+
noSelect: unselectable,
|
|
322
|
+
codeFoldGutter,
|
|
323
|
+
codeFoldContentContainer,
|
|
324
|
+
codeFold,
|
|
325
|
+
emptyGutter,
|
|
326
|
+
emptyLine,
|
|
327
|
+
lineNumber,
|
|
328
|
+
contentText,
|
|
329
|
+
content,
|
|
330
|
+
column,
|
|
331
|
+
codeFoldContent,
|
|
332
|
+
titleBlock,
|
|
333
|
+
};
|
|
334
|
+
const computerOverrideStyles = Object.keys(styles).reduce((acc, key) => ({
|
|
335
|
+
...acc,
|
|
336
|
+
...{
|
|
337
|
+
[key]: css(styles[key]),
|
|
338
|
+
},
|
|
339
|
+
}), {});
|
|
340
|
+
return Object.keys(defaultStyles).reduce((acc, key) => ({
|
|
341
|
+
...acc,
|
|
342
|
+
...{
|
|
343
|
+
[key]: computerOverrideStyles[key]
|
|
344
|
+
? cx(defaultStyles[key], computerOverrideStyles[key])
|
|
345
|
+
: defaultStyles[key],
|
|
346
|
+
},
|
|
347
|
+
}), {});
|
|
348
|
+
};
|