Skip to content

Commit 47a0910

Browse files
committed
feat: diff summary
1 parent 7cc61d0 commit 47a0910

File tree

2 files changed

+141
-0
lines changed

2 files changed

+141
-0
lines changed
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
import { Meta, StoryObj } from '@storybook/react'
2+
import DiffSummary from '.'
3+
4+
const meta: Meta<typeof DiffSummary> = {
5+
component: DiffSummary,
6+
}
7+
8+
export default meta
9+
10+
type Story = StoryObj<typeof DiffSummary>
11+
12+
export const Default: Story = {
13+
args: {
14+
addedLines: 10,
15+
removedLines: 5,
16+
squareCount: 5,
17+
},
18+
}
19+
20+
export const ManyLineChanges: Story = {
21+
args: {
22+
addedLines: 1500,
23+
removedLines: 2050,
24+
squareCount: 5,
25+
},
26+
}
27+
28+
export const NoChanges: Story = {
29+
args: {
30+
addedLines: 0,
31+
removedLines: 0,
32+
squareCount: 5,
33+
},
34+
}
35+
36+
export const NoLabel: Story = {
37+
args: {
38+
addedLines: 10,
39+
removedLines: 5,
40+
squareCount: 5,
41+
showLabel: false,
42+
},
43+
}
44+
45+
export const CustomSquareCount: Story = {
46+
args: {
47+
addedLines: 10,
48+
removedLines: 5,
49+
squareCount: 10,
50+
},
51+
}
52+
53+
export const CustomSquareClassName: Story = {
54+
args: {
55+
addedLines: 10,
56+
removedLines: 5,
57+
squareCount: 5,
58+
squareClassName: 'size-4',
59+
},
60+
}
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
import { cn } from '@/lib/utils'
2+
3+
interface DiffSummaryProps {
4+
addedLines: number
5+
removedLines: number
6+
squareCount?: number
7+
squareClassName?: string
8+
showLabel?: boolean
9+
}
10+
11+
export default function DiffSummary({
12+
addedLines,
13+
removedLines,
14+
squareCount = 5,
15+
squareClassName,
16+
showLabel = true,
17+
}: DiffSummaryProps) {
18+
const totalLines = addedLines + removedLines
19+
const filledAddedSquares =
20+
totalLines > 0 ? Math.round((addedLines / totalLines) * squareCount) : 0
21+
const filledRemovedSquares =
22+
totalLines > 0 ? Math.round((removedLines / totalLines) * squareCount) : 0
23+
24+
// Ensure the total does not exceed squareCount
25+
let greenCount = filledAddedSquares
26+
let redCount = filledRemovedSquares
27+
if (greenCount + redCount > squareCount) {
28+
const overflow = greenCount + redCount - squareCount
29+
if (greenCount > redCount) {
30+
greenCount -= overflow
31+
} else {
32+
redCount -= overflow
33+
}
34+
}
35+
36+
const baseSquareClasses = cn('w-3.5 h-3.5 rounded-sm', squareClassName)
37+
38+
const squares = Array.from({ length: squareCount }, (_, index) => {
39+
if (index < greenCount) {
40+
return (
41+
<div
42+
key={index}
43+
className={cn(
44+
baseSquareClasses,
45+
'bg-emerald-500 dark:bg-emerald-400'
46+
)}
47+
/>
48+
)
49+
} else if (index < greenCount + redCount) {
50+
return (
51+
<div
52+
key={index}
53+
className={cn(baseSquareClasses, 'bg-rose-500 dark:bg-rose-400')}
54+
/>
55+
)
56+
} else {
57+
return (
58+
<div
59+
key={index}
60+
className={cn(baseSquareClasses, 'bg-gray-200 dark:bg-gray-200')}
61+
/>
62+
)
63+
}
64+
})
65+
66+
return (
67+
<div className="flex items-center gap-1.5">
68+
{showLabel && (
69+
<span className="flex flex-row gap-1.5 text-sm font-semibold">
70+
<span className="text-emerald-500 dark:text-emerald-400">
71+
+{addedLines}
72+
</span>
73+
<span className="text-rose-500 dark:text-rose-400">
74+
-{removedLines}
75+
</span>
76+
</span>
77+
)}
78+
<div className="flex items-center gap-0.5">{squares}</div>
79+
</div>
80+
)
81+
}

0 commit comments

Comments
 (0)