From 47a0910344a58c3d1bd17c1ceec73d848ca88a24 Mon Sep 17 00:00:00 2001 From: Adam Bull Date: Fri, 13 Jun 2025 14:02:21 +0100 Subject: [PATCH] feat: diff summary --- src/components/DiffSummary/index.stories.tsx | 60 +++++++++++++++ src/components/DiffSummary/index.tsx | 81 ++++++++++++++++++++ 2 files changed, 141 insertions(+) create mode 100644 src/components/DiffSummary/index.stories.tsx create mode 100644 src/components/DiffSummary/index.tsx diff --git a/src/components/DiffSummary/index.stories.tsx b/src/components/DiffSummary/index.stories.tsx new file mode 100644 index 00000000..2111d6eb --- /dev/null +++ b/src/components/DiffSummary/index.stories.tsx @@ -0,0 +1,60 @@ +import { Meta, StoryObj } from '@storybook/react' +import DiffSummary from '.' + +const meta: Meta = { + component: DiffSummary, +} + +export default meta + +type Story = StoryObj + +export const Default: Story = { + args: { + addedLines: 10, + removedLines: 5, + squareCount: 5, + }, +} + +export const ManyLineChanges: Story = { + args: { + addedLines: 1500, + removedLines: 2050, + squareCount: 5, + }, +} + +export const NoChanges: Story = { + args: { + addedLines: 0, + removedLines: 0, + squareCount: 5, + }, +} + +export const NoLabel: Story = { + args: { + addedLines: 10, + removedLines: 5, + squareCount: 5, + showLabel: false, + }, +} + +export const CustomSquareCount: Story = { + args: { + addedLines: 10, + removedLines: 5, + squareCount: 10, + }, +} + +export const CustomSquareClassName: Story = { + args: { + addedLines: 10, + removedLines: 5, + squareCount: 5, + squareClassName: 'size-4', + }, +} diff --git a/src/components/DiffSummary/index.tsx b/src/components/DiffSummary/index.tsx new file mode 100644 index 00000000..b693c0ad --- /dev/null +++ b/src/components/DiffSummary/index.tsx @@ -0,0 +1,81 @@ +import { cn } from '@/lib/utils' + +interface DiffSummaryProps { + addedLines: number + removedLines: number + squareCount?: number + squareClassName?: string + showLabel?: boolean +} + +export default function DiffSummary({ + addedLines, + removedLines, + squareCount = 5, + squareClassName, + showLabel = true, +}: DiffSummaryProps) { + const totalLines = addedLines + removedLines + const filledAddedSquares = + totalLines > 0 ? Math.round((addedLines / totalLines) * squareCount) : 0 + const filledRemovedSquares = + totalLines > 0 ? Math.round((removedLines / totalLines) * squareCount) : 0 + + // Ensure the total does not exceed squareCount + let greenCount = filledAddedSquares + let redCount = filledRemovedSquares + if (greenCount + redCount > squareCount) { + const overflow = greenCount + redCount - squareCount + if (greenCount > redCount) { + greenCount -= overflow + } else { + redCount -= overflow + } + } + + const baseSquareClasses = cn('w-3.5 h-3.5 rounded-sm', squareClassName) + + const squares = Array.from({ length: squareCount }, (_, index) => { + if (index < greenCount) { + return ( +
+ ) + } else if (index < greenCount + redCount) { + return ( +
+ ) + } else { + return ( +
+ ) + } + }) + + return ( +
+ {showLabel && ( + + + +{addedLines} + + + -{removedLines} + + + )} +
{squares}
+
+ ) +}