Skip to content

Commit bd0ec0b

Browse files
authored
Merge pull request #22 from utkuakyuz/feature/accessible-diff-objects
feature: accessible difference data with callback
2 parents 8fbb2ed + c38ea84 commit bd0ec0b

File tree

6 files changed

+100
-22
lines changed

6 files changed

+100
-22
lines changed

README.md

Lines changed: 53 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -49,9 +49,9 @@ The theme is fully customizable, all colors can be changed. (And soon new themes
4949

5050
Modify DifferOptions and InlineDiffOptions and see the output.
5151

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

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

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

6969
```
70-
import React from "react";
7170
import { VirtualDiffViewer } from "virtual-react-json-diff";
7271
7372
const oldData = { name: "Alice", age: 25 };
@@ -85,6 +84,39 @@ export default function App() {
8584
}
8685
```
8786

87+
---
88+
89+
If you need to see or make some calculations on difference objects, you can get the diff data using `getDifferData` callback prop
90+
91+
```
92+
import { type DiffResult, VirtualDiffViewer } from "virtual-react-json-diff";
93+
94+
const [differData, setDifferData] = useState<[DiffResult[], DiffResult[]]>();
95+
96+
<VirtualDiffViewer {...props} getDiffData={diffData => setDifferData(diffData)} />
97+
```
98+
99+
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`.
100+
101+
p.s. This is not recommended because you can modify all variables in Differ using `differOptions` prop in Viewer.
102+
103+
```
104+
import { Differ, VirtualDiffViewer } from "virtual-react-json-diff";
105+
---
106+
const differOptions: DifferOptions = {
107+
showModifications: config.showModifications,
108+
arrayDiffMethod: config.arrayDiffMethod,
109+
};
110+
const differ = new Differ(differOptions);
111+
112+
---
113+
114+
// Pass it into Viewer with 'customDiffer' prop
115+
<VirtualDiffViewer {...props} customDiffer={differ} />
116+
```
117+
118+
---
119+
88120
The component exposes a root container with the class:
89121

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

96128
## Props
97129

98-
| Prop | Type | Default | Description |
99-
| ------------------- | ------------------------- | ------------------ | ---------------------------------------------------------------------- |
100-
| `oldValue` | `object` || The original JSON object to compare (left side). |
101-
| `newValue` | `object` || The updated JSON object to compare (right side). |
102-
| `height` | `number` || Height of the diff viewer in pixels. |
103-
| `hideSearch` | `boolean` | `false` | Hides the search bar if set to `true`. |
104-
| `searchTerm` | `string` | `""` | Initial search keyword to highlight within the diff. |
105-
| `leftTitle` | `string` || Optional title to display above the left diff panel. |
106-
| `rightTitle` | `string` || Optional title to display above the right diff panel. |
107-
| `onSearchMatch` | `(index: number) => void` || Callback fired when a search match is found. Receives the match index. |
108-
| `differOptions` | `DifferOptions` | `Given Above` | Advanced options passed to the diffing engine. |
109-
| `showSingleMinimap` | `boolean` | `false` | If `true`, shows only one minimap instead of two. |
110-
| `className` | `string` || Custom CSS class for styling the viewer container. |
111-
| `miniMapWidth` | `number` | `40` | Width of each minimap in pixels. |
112-
| `inlineDiffOptions` | `InlineDiffOptions` | `{'mode': 'char'}` | Options for fine-tuning inline diff rendering. |
130+
| Prop | Type | Default | Description |
131+
| ------------------- | -------------------------------------------------- | ------------------ | ---------------------------------------------------------------------- |
132+
| `oldValue` | `object` || The original JSON object to compare (left side). |
133+
| `newValue` | `object` || The updated JSON object to compare (right side). |
134+
| `height` | `number` || Height of the diff viewer in pixels. |
135+
| `hideSearch` | `boolean` | `false` | Hides the search bar if set to `true`. |
136+
| `searchTerm` | `string` | `""` | Initial search keyword to highlight within the diff. |
137+
| `leftTitle` | `string` || Optional title to display above the left diff panel. |
138+
| `rightTitle` | `string` || Optional title to display above the right diff panel. |
139+
| `onSearchMatch` | `(index: number) => void` || Callback fired when a search match is found. Receives the match index. |
140+
| `differOptions` | `DifferOptions` | `Given Above` | Advanced options passed to the diffing engine. |
141+
| `showSingleMinimap` | `boolean` | `false` | If `true`, shows only one minimap instead of two. |
142+
| `className` | `string` || Custom CSS class for styling the viewer container. |
143+
| `overScanCount` | `number` | `28` | Number of rendered rows outside of the viewport for virtualization |
144+
| `miniMapWidth` | `number` | `40` | Width of each minimap in pixels. |
145+
| `inlineDiffOptions` | `InlineDiffOptions` | `{'mode': 'char'}` | Options for fine-tuning inline diff rendering. |
146+
| `getDiffData` | `(diffData: [DiffResult[], DiffResult[]]) => void` | - | Get difference data and make operations |
147+
| `customDiffer` | `Differ` | - | Pass custom differ - not recommended |
113148

114149
## 🙌 Acknowledgements
115150

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"name": "virtual-react-json-diff",
33
"type": "module",
4-
"version": "1.0.12",
4+
"version": "1.0.13",
55
"description": "Fast, virtualized React component for visually comparing large JSON objects. Includes search, theming, and minimap.",
66
"author": {
77
"name": "Utku Akyüz"

src/components/DiffViewer/components/VirtualizedDiffViewer.tsx

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import type { DiffResult } from "json-diff-kit";
12
import type { VariableSizeList as List } from "react-window";
23

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

89
import "../styles/JsonDiffCustomTheme.css";
910
import { useSearch } from "../hooks/useSearch";
11+
import { fastHash } from "../utils/json-diff/diff-hash";
1012
import { expandSegment, hasExpandedSegments, hideAllSegments } from "../utils/json-diff/segment-util";
1113
import { buildViewFromSegments, generateSegments } from "../utils/preprocessDiff";
1214
import { DiffMinimap } from "./DiffMinimap";
@@ -21,6 +23,8 @@ export const VirtualizedDiffViewer: React.FC<VirtualizedDiffViewerProps> = ({
2123
leftTitle,
2224
rightTitle,
2325
hideSearch,
26+
customDiffer,
27+
getDiffData,
2428
showSingleMinimap,
2529
onSearchMatch,
2630
differOptions,
@@ -31,8 +35,10 @@ export const VirtualizedDiffViewer: React.FC<VirtualizedDiffViewerProps> = ({
3135
}) => {
3236
const outerRef = useRef<Node>(null);
3337
const listRef = useRef<List>(null);
38+
const getDiffDataRef = useRef<typeof getDiffData>();
39+
const lastSent = useRef<number>();
3440

35-
const differ = useMemo(
41+
const differ = customDiffer ?? useMemo(
3642
() =>
3743
new Differ({
3844
detectCircular: true,
@@ -101,6 +107,23 @@ export const VirtualizedDiffViewer: React.FC<VirtualizedDiffViewerProps> = ({
101107
setRightView(rightBuilt);
102108
}, [segments, rawLeftDiff, rawRightDiff]);
103109

110+
useEffect(() => {
111+
getDiffDataRef.current = getDiffData;
112+
}, [getDiffData]);
113+
114+
useEffect(() => {
115+
if (!getDiffDataRef.current)
116+
return;
117+
118+
const data: [DiffResult[], DiffResult[]] = [rawLeftDiff, rawRightDiff];
119+
const hash = fastHash(data);
120+
121+
if (lastSent.current !== hash) {
122+
lastSent.current = hash;
123+
getDiffDataRef.current(data);
124+
}
125+
}, [rawLeftDiff, rawRightDiff]);
126+
104127
return (
105128
<div className={`diff-viewer-container${className ? ` ${className}` : ""}`}>
106129

src/components/DiffViewer/types/index.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import type { DifferOptions, DiffResult, InlineDiffOptions } from "json-diff-kit";
1+
import type { Differ, DifferOptions, DiffResult, InlineDiffOptions } from "json-diff-kit";
22

33
export type DiffRow = {
44
originalIndex: number;
@@ -41,7 +41,9 @@ export type VirtualizedDiffViewerProps = {
4141
leftTitle?: string;
4242
rightTitle?: string;
4343
onSearchMatch?: (index: number) => void;
44+
getDiffData?: (diffData: [DiffResult[], DiffResult[]]) => void;
4445
differOptions?: DifferOptions;
46+
customDiffer?: Differ;
4547
showSingleMinimap?: boolean;
4648
className?: string;
4749
miniMapWidth?: number;
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
import type { DiffResult } from "json-diff-kit";
2+
3+
export function fastHash(diff: [DiffResult[], DiffResult[]]) {
4+
let hash = 5381; // djb2 seed
5+
for (const arr of diff) {
6+
for (const row of arr) {
7+
const text = row.text;
8+
for (let i = 0; i < text.length; i++) {
9+
hash = ((hash << 5) + hash) + text.charCodeAt(i);
10+
}
11+
const type = row.type;
12+
for (let i = 0; i < type.length; i++) {
13+
hash = ((hash << 5) + hash) + type.charCodeAt(i);
14+
}
15+
}
16+
}
17+
return hash >>> 0;
18+
}

src/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
1-
// Buttons
21
export { default as VirtualDiffViewer } from "./components/DiffViewer";
2+
export { Differ, type DiffResult } from "json-diff-kit";

0 commit comments

Comments
 (0)