Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

feature: accessible difference data with callback #22

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
utkuakyuz merged 4 commits into main from feature/accessible-diff-objects
Sep 16, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
71 changes: 53 additions & 18 deletions README.md
View file Open in desktop
Original file line number Diff line number Diff line change
Expand Up @@ -49,9 +49,9 @@ The theme is fully customizable, all colors can be changed. (And soon new themes

Modify DifferOptions and InlineDiffOptions and see the output.

Dual Minimap is defaultly shown, to hide middle minimap, just pass ShowSingleMinimap prop to Viewer.
Dual Minimap is defaultly shown, to hide middle minimap, just pass `ShowSingleMinimap` prop to Viewer.

To change Diff methods please see DifferOptions. By default virtual-react-json-diff uses following configuration.
To change Diff methods please see `DifferOptions`. By default `virtual-react-json-diff` uses following configuration.

```
new Differ({
Expand All @@ -67,7 +67,6 @@ new Differ({
Simply pass your json objects into Viewer Component. It will find differences and show.

```
import React from "react";
import { VirtualDiffViewer } from "virtual-react-json-diff";

const oldData = { name: "Alice", age: 25 };
Expand All @@ -85,6 +84,39 @@ export default function App() {
}
```

---

If you need to see or make some calculations on difference objects, you can get the diff data using `getDifferData` callback prop

```
import { type DiffResult, VirtualDiffViewer } from "virtual-react-json-diff";

const [differData, setDifferData] = useState<[DiffResult[], DiffResult[]]>();

<VirtualDiffViewer {...props} getDiffData={diffData => setDifferData(diffData)} />
```

Or if you have a custom Differ or a custom viewer, you can import `Differ` class to create diff objects using your own differ. Moreover you can pass that differ to `VirtualizedDiffViewer`.

p.s. This is not recommended because you can modify all variables in Differ using `differOptions` prop in Viewer.

```
import { Differ, VirtualDiffViewer } from "virtual-react-json-diff";
---
const differOptions: DifferOptions = {
showModifications: config.showModifications,
arrayDiffMethod: config.arrayDiffMethod,
};
const differ = new Differ(differOptions);

---

// Pass it into Viewer with 'customDiffer' prop
<VirtualDiffViewer {...props} customDiffer={differ} />
```

---

The component exposes a root container with the class:

```
Expand All @@ -95,21 +127,24 @@ You can pass your own class name via the className prop to apply custom themes.

## Props

| Prop | Type | Default | Description |
| ------------------- | ------------------------- | ------------------ | ---------------------------------------------------------------------- |
| `oldValue` | `object` | — | The original JSON object to compare (left side). |
| `newValue` | `object` | — | The updated JSON object to compare (right side). |
| `height` | `number` | — | Height of the diff viewer in pixels. |
| `hideSearch` | `boolean` | `false` | Hides the search bar if set to `true`. |
| `searchTerm` | `string` | `""` | Initial search keyword to highlight within the diff. |
| `leftTitle` | `string` | — | Optional title to display above the left diff panel. |
| `rightTitle` | `string` | — | Optional title to display above the right diff panel. |
| `onSearchMatch` | `(index: number) => void` | — | Callback fired when a search match is found. Receives the match index. |
| `differOptions` | `DifferOptions` | `Given Above` | Advanced options passed to the diffing engine. |
| `showSingleMinimap` | `boolean` | `false` | If `true`, shows only one minimap instead of two. |
| `className` | `string` | — | Custom CSS class for styling the viewer container. |
| `miniMapWidth` | `number` | `40` | Width of each minimap in pixels. |
| `inlineDiffOptions` | `InlineDiffOptions` | `{'mode': 'char'}` | Options for fine-tuning inline diff rendering. |
| Prop | Type | Default | Description |
| ------------------- | -------------------------------------------------- | ------------------ | ---------------------------------------------------------------------- |
| `oldValue` | `object` | — | The original JSON object to compare (left side). |
| `newValue` | `object` | — | The updated JSON object to compare (right side). |
| `height` | `number` | — | Height of the diff viewer in pixels. |
| `hideSearch` | `boolean` | `false` | Hides the search bar if set to `true`. |
| `searchTerm` | `string` | `""` | Initial search keyword to highlight within the diff. |
| `leftTitle` | `string` | — | Optional title to display above the left diff panel. |
| `rightTitle` | `string` | — | Optional title to display above the right diff panel. |
| `onSearchMatch` | `(index: number) => void` | — | Callback fired when a search match is found. Receives the match index. |
| `differOptions` | `DifferOptions` | `Given Above` | Advanced options passed to the diffing engine. |
| `showSingleMinimap` | `boolean` | `false` | If `true`, shows only one minimap instead of two. |
| `className` | `string` | — | Custom CSS class for styling the viewer container. |
| `overScanCount` | `number` | `28` | Number of rendered rows outside of the viewport for virtualization |
| `miniMapWidth` | `number` | `40` | Width of each minimap in pixels. |
| `inlineDiffOptions` | `InlineDiffOptions` | `{'mode': 'char'}` | Options for fine-tuning inline diff rendering. |
| `getDiffData` | `(diffData: [DiffResult[], DiffResult[]]) => void` | - | Get difference data and make operations |
| `customDiffer` | `Differ` | - | Pass custom differ - not recommended |

## 🙌 Acknowledgements

Expand Down
2 changes: 1 addition & 1 deletion package.json
View file Open in desktop
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "virtual-react-json-diff",
"type": "module",
"version": "1.0.12",
"version": "1.0.13",
"description": "Fast, virtualized React component for visually comparing large JSON objects. Includes search, theming, and minimap.",
"author": {
"name": "Utku Akyüz"
Expand Down
25 changes: 24 additions & 1 deletion src/components/DiffViewer/components/VirtualizedDiffViewer.tsx
View file Open in desktop
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import type { DiffResult } from "json-diff-kit";
import type { VariableSizeList as List } from "react-window";

import { Differ } from "json-diff-kit";
Expand All @@ -7,6 +8,7 @@ import type { DiffRowOrCollapsed, SegmentItem, VirtualizedDiffViewerProps } from

import "../styles/JsonDiffCustomTheme.css";
import { useSearch } from "../hooks/useSearch";
import { fastHash } from "../utils/json-diff/diff-hash";
import { expandSegment, hasExpandedSegments, hideAllSegments } from "../utils/json-diff/segment-util";
import { buildViewFromSegments, generateSegments } from "../utils/preprocessDiff";
import { DiffMinimap } from "./DiffMinimap";
Expand All @@ -21,6 +23,8 @@ export const VirtualizedDiffViewer: React.FC<VirtualizedDiffViewerProps> = ({
leftTitle,
rightTitle,
hideSearch,
customDiffer,
getDiffData,
showSingleMinimap,
onSearchMatch,
differOptions,
Expand All @@ -31,8 +35,10 @@ export const VirtualizedDiffViewer: React.FC<VirtualizedDiffViewerProps> = ({
}) => {
const outerRef = useRef<Node>(null);
const listRef = useRef<List>(null);
const getDiffDataRef = useRef<typeof getDiffData>();
const lastSent = useRef<number>();

const differ = useMemo(
const differ = customDiffer ?? useMemo(
() =>
new Differ({
detectCircular: true,
Expand Down Expand Up @@ -101,6 +107,23 @@ export const VirtualizedDiffViewer: React.FC<VirtualizedDiffViewerProps> = ({
setRightView(rightBuilt);
}, [segments, rawLeftDiff, rawRightDiff]);

useEffect(() => {
getDiffDataRef.current = getDiffData;
}, [getDiffData]);

useEffect(() => {
if (!getDiffDataRef.current)
return;

const data: [DiffResult[], DiffResult[]] = [rawLeftDiff, rawRightDiff];
const hash = fastHash(data);

if (lastSent.current !== hash) {
lastSent.current = hash;
getDiffDataRef.current(data);
}
}, [rawLeftDiff, rawRightDiff]);

return (
<div className={`diff-viewer-container${className ? ` ${className}` : ""}`}>

Expand Down
4 changes: 3 additions & 1 deletion src/components/DiffViewer/types/index.ts
View file Open in desktop
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import type { DifferOptions, DiffResult, InlineDiffOptions } from "json-diff-kit";
import type { Differ, DifferOptions, DiffResult, InlineDiffOptions } from "json-diff-kit";

export type DiffRow = {
originalIndex: number;
Expand Down Expand Up @@ -41,7 +41,9 @@ export type VirtualizedDiffViewerProps = {
leftTitle?: string;
rightTitle?: string;
onSearchMatch?: (index: number) => void;
getDiffData?: (diffData: [DiffResult[], DiffResult[]]) => void;
differOptions?: DifferOptions;
customDiffer?: Differ;
showSingleMinimap?: boolean;
className?: string;
miniMapWidth?: number;
Expand Down
18 changes: 18 additions & 0 deletions src/components/DiffViewer/utils/json-diff/diff-hash.ts
View file Open in desktop
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import type { DiffResult } from "json-diff-kit";

export function fastHash(diff: [DiffResult[], DiffResult[]]) {
let hash = 5381; // djb2 seed
for (const arr of diff) {
for (const row of arr) {
const text = row.text;
for (let i = 0; i < text.length; i++) {
hash = ((hash << 5) + hash) + text.charCodeAt(i);
}
const type = row.type;
for (let i = 0; i < type.length; i++) {
hash = ((hash << 5) + hash) + type.charCodeAt(i);
}
}
}
return hash >>> 0;
}
2 changes: 1 addition & 1 deletion src/index.ts
View file Open in desktop
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
// Buttons
export { default as VirtualDiffViewer } from "./components/DiffViewer";
export { Differ, type DiffResult } from "json-diff-kit";

AltStyle によって変換されたページ (->オリジナル) /