Skip to content

Commit 3a45094

Browse files
committed
feat: add line count display feature to diff viewer
- Introduced a new configuration option to show line counts in the diff viewer. - Added a LineCountDisplay component to visualize added, removed, and modified lines. - Updated the VirtualizedDiffViewer to calculate and display line count statistics based on the diff data. - Enhanced the Sidebar component to include a checkbox for toggling the line count display. - Updated styles for the new line count display feature.
1 parent 18809ca commit 3a45094

File tree

8 files changed

+176
-1
lines changed

8 files changed

+176
-1
lines changed

demo/src/App.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ export default function App() {
2525
miniMapWidth: 20,
2626
hideSearch: false,
2727
height: 380,
28+
showLineCount: true,
2829

2930
// Differ Configuration
3031
detectCircular: true,
@@ -219,6 +220,7 @@ export default function App() {
219220
height={config.height}
220221
miniMapWidth={config.miniMapWidth}
221222
hideSearch={config.hideSearch}
223+
showLineCount={config.showLineCount}
222224
inlineDiffOptions={{ mode: config.inlineDiffMode }}
223225
oldValue={parsedOldValue}
224226
newValue={parsedNewValue}

demo/src/components/Sidebar.tsx

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,19 @@ function Sidebar(props: Props) {
9494
</label>
9595
</div>
9696

97+
<div className="form-group">
98+
<label className="checkbox-label">
99+
<input
100+
type="checkbox"
101+
className="form-checkbox"
102+
checked={config.showLineCount}
103+
onChange={e => updateConfig("showLineCount", e.target.checked)}
104+
/>
105+
Show Line Count
106+
</label>
107+
<p className="form-hint">Display statistics for added, removed, and modified lines</p>
108+
</div>
109+
97110
<div className="form-group">
98111
<label className="form-label">
99112
CSS Class Name

demo/src/types/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ export type Config = {
88
miniMapWidth: number;
99
hideSearch: boolean;
1010
height: number;
11+
showLineCount: boolean;
1112

1213
// Differ Configuration
1314
detectCircular: boolean;
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
import React from "react";
2+
3+
import type { LineCountStats } from "../types";
4+
5+
type LineCountDisplayProps = {
6+
stats: LineCountStats;
7+
};
8+
9+
export const LineCountDisplay: React.FC<LineCountDisplayProps> = ({ stats }) => {
10+
if (stats.total === 0) {
11+
return (
12+
<div className="line-count-display">
13+
<span className="line-count-item no-changes">No changes</span>
14+
</div>
15+
);
16+
}
17+
18+
return (
19+
<div className="line-count-display">
20+
{stats.added > 0 && (
21+
<span className="line-count-item added">
22+
+
23+
{stats.added}
24+
{" "}
25+
added
26+
</span>
27+
)}
28+
{stats.removed > 0 && (
29+
<span className="line-count-item removed">
30+
-
31+
{stats.removed}
32+
{" "}
33+
removed
34+
</span>
35+
)}
36+
{stats.modified > 0 && (
37+
<span className="line-count-item modified">
38+
~
39+
{stats.modified}
40+
{" "}
41+
modified
42+
</span>
43+
)}
44+
<span className="line-count-item total">
45+
{stats.total}
46+
{" "}
47+
total changes
48+
</span>
49+
</div>
50+
);
51+
};
52+
53+
export default LineCountDisplay;

src/components/DiffViewer/components/VirtualizedDiffViewer.tsx

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,16 @@ import type { VariableSizeList as List } from "react-window";
44
import { Differ } from "json-diff-kit";
55
import React, { useCallback, useEffect, useMemo, useRef, useState } from "react";
66

7-
import type { DiffRowOrCollapsed, SegmentItem, VirtualizedDiffViewerProps } from "../types";
7+
import type { DiffRowOrCollapsed, LineCountStats, SegmentItem, VirtualizedDiffViewerProps } from "../types";
88

99
import "../styles/JsonDiffCustomTheme.css";
1010
import { useSearch } from "../hooks/useSearch";
1111
import { fastHash } from "../utils/json-diff/diff-hash";
1212
import { expandSegment, hasExpandedSegments, hideAllSegments } from "../utils/json-diff/segment-util";
13+
import { calculateLineCountStats } from "../utils/lineCountUtils";
1314
import { buildViewFromSegments, generateSegments } from "../utils/preprocessDiff";
1415
import { DiffMinimap } from "./DiffMinimap";
16+
import LineCountDisplay from "./LineCountDisplay";
1517
import SearchboxHolder from "./SearchboxHolder";
1618
import VirtualDiffGrid from "./VirtualDiffGrid";
1719

@@ -32,6 +34,7 @@ export const VirtualizedDiffViewer: React.FC<VirtualizedDiffViewerProps> = ({
3234
miniMapWidth,
3335
inlineDiffOptions,
3436
overScanCount,
37+
showLineCount = false,
3538
}) => {
3639
const listRef = useRef<List>(null);
3740
const getDiffDataRef = useRef<typeof getDiffData>();
@@ -58,6 +61,13 @@ export const VirtualizedDiffViewer: React.FC<VirtualizedDiffViewerProps> = ({
5861
return differ.diff(oldValue, newValue);
5962
}, [oldValue, newValue, differ]);
6063

64+
const lineCountStats = useMemo((): LineCountStats => {
65+
if (!diffData || (diffData[0].length === 0 && diffData[1].length === 0)) {
66+
return { added: 0, removed: 0, modified: 0, total: 0 };
67+
}
68+
return calculateLineCountStats(diffData as [DiffResult[], DiffResult[]]);
69+
}, [diffData]);
70+
6171
const [scrollTop, setScrollTop] = useState(0);
6272
const [segments, setSegments] = useState<SegmentItem[]>([]);
6373
const [rawLeftDiff, rawRightDiff] = diffData;
@@ -138,6 +148,10 @@ export const VirtualizedDiffViewer: React.FC<VirtualizedDiffViewerProps> = ({
138148
<div><span>{leftTitle}</span></div>
139149
<div><span>{rightTitle}</span></div>
140150
</div>
151+
152+
{showLineCount && (
153+
<LineCountDisplay stats={lineCountStats} />
154+
)}
141155
</div>
142156

143157
{/* List & Minimap */}

src/components/DiffViewer/styles/JsonDiffCustomTheme.css

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,53 @@
4242
background: rgba(182, 180, 67, 0.08);
4343
}
4444

45+
/* LINE COUNT DISPLAY */
46+
.line-count-display {
47+
display: flex;
48+
gap: 12px;
49+
align-items: center;
50+
font-size: 11px;
51+
color: #f8f8f2;
52+
margin-left: auto;
53+
}
54+
55+
.line-count-item {
56+
padding: 2px 6px;
57+
border-radius: 3px;
58+
font-weight: 500;
59+
white-space: nowrap;
60+
}
61+
62+
.line-count-item.added {
63+
background: rgba(100, 182, 67, 0.2);
64+
color: #a5ff99;
65+
border: 1px solid rgba(100, 182, 67, 0.3);
66+
}
67+
68+
.line-count-item.removed {
69+
background: rgba(160, 128, 100, 0.2);
70+
color: #ffaa99;
71+
border: 1px solid rgba(160, 128, 100, 0.3);
72+
}
73+
74+
.line-count-item.modified {
75+
background: rgba(182, 180, 67, 0.2);
76+
color: #ecff99;
77+
border: 1px solid rgba(182, 180, 67, 0.3);
78+
}
79+
80+
.line-count-item.total {
81+
background: rgba(69, 96, 248, 0.2);
82+
color: #4560f8;
83+
border: 1px solid rgba(69, 96, 248, 0.3);
84+
}
85+
86+
.line-count-item.no-changes {
87+
background: rgba(248, 248, 242, 0.1);
88+
color: #f8f8f2;
89+
border: 1px solid rgba(248, 248, 242, 0.2);
90+
}
91+
4592
.json-diff-viewer.json-diff-viewer-theme-custom .empty-equal-cell {
4693
opacity: 0.4;
4794
background: repeating-linear-gradient(-53deg, rgb(69, 69, 70), rgb(69, 69, 70) 1.5px, #282a36 1.5px, #282a36 4px);

src/components/DiffViewer/types/index.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,13 @@ export type SearchState = {
3232
currentIndex: number;
3333
};
3434

35+
export type LineCountStats = {
36+
added: number;
37+
removed: number;
38+
modified: number;
39+
total: number;
40+
};
41+
3542
export type VirtualizedDiffViewerProps = {
3643
oldValue: object;
3744
newValue: object;
@@ -49,6 +56,7 @@ export type VirtualizedDiffViewerProps = {
4956
miniMapWidth?: number;
5057
inlineDiffOptions?: InlineDiffOptions;
5158
overScanCount?: number;
59+
showLineCount?: boolean;
5260
};
5361

5462
export type DiffMinimapProps = {
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
import type { DiffResult } from "json-diff-kit";
2+
3+
import type { LineCountStats } from "../types";
4+
5+
export function calculateLineCountStats(diffData: [DiffResult[], DiffResult[]]): LineCountStats {
6+
const [leftDiff, rightDiff] = diffData;
7+
8+
let added = 0;
9+
let removed = 0;
10+
let modified = 0;
11+
12+
// Count changes from the left diff (removed lines)
13+
for (const line of leftDiff) {
14+
if (line.type === "remove") {
15+
removed++;
16+
}
17+
else if (line.type === "modify") {
18+
modified++;
19+
}
20+
}
21+
22+
// Count changes from the right diff (added lines)
23+
for (const line of rightDiff) {
24+
if (line.type === "add") {
25+
added++;
26+
}
27+
}
28+
29+
const total = added + removed + modified;
30+
31+
return {
32+
added,
33+
removed,
34+
modified,
35+
total,
36+
};
37+
}

0 commit comments

Comments
 (0)