Giter Club home page Giter Club logo

react-diff-viewer-continued's Introduction

React Diff Viewer


All Contributors

npm version npm downloads GitHub license

A simple and beautiful text diff viewer component made with Diff and React.

example image

Inspired by the Github diff viewer, it includes features like split view, inline view, word diff, line highlight and more. It is highly customizable and it supports almost all languages.

Most credit goes to Pranesh Ravi who created the original diff viewer. I've just made a few modifications and updated the dependencies so they work with modern stacks.

Install

yarn add react-diff-viewer-continued

# or

npm i react-diff-viewer-continued

# or

pnpm add react-diff-viewer-continued

Usage

import React, { PureComponent } from 'react';
import ReactDiffViewer from 'react-diff-viewer-continued';

const oldCode = `
const a = 10
const b = 10
const c = () => console.log('foo')

if(a > 10) {
  console.log('bar')
}

console.log('done')
`;
const newCode = `
const a = 10
const boo = 10

if(a === 10) {
  console.log('bar')
}
`;

class Diff extends PureComponent {
  render = () => {
    return (
      <ReactDiffViewer oldValue={oldCode} newValue={newCode} splitView={true} />
    );
  };
}

Props

Prop Type Default Description
oldValue string | Object '' Old value as string (or Object if using diffJson).
newValue string | Object '' New value as string (or Object if using diffJson).
splitView boolean true Switch between unified and split view.
disableWordDiff boolean false Show and hide word diff in a diff line.
compareMethod DiffMethod | (string, string) => diff.Change[] DiffMethod.CHARS Uses an existing diff method when a DiffMethod enum is passed. If a function is passed, that function is used as the diff method.

JsDiff text diff method used for diffing strings. Check out the guide to use different methods.
renderGutter (diffData) => ReactNode undefined Function that can be used to render an extra gutter with various information next to the line number.
hideLineNumbers boolean false Show and hide line numbers.
alwaysShowLines string[] [] List of lines to always be shown, regardless of diff status. Line number are prefixed with L and R for the left and right section of the diff viewer, respectively. For example, L-20 means 20th line in the left pane. extraLinesSurroundingDiff applies to these lines as well.
renderContent function undefined Render Prop API to render code in the diff viewer. Helpful for syntax highlighting
onLineNumberClick function undefined Event handler for line number click. (lineId: string) => void
highlightLines string[] [] List of lines to be highlighted. Works together with onLineNumberClick. Line number are prefixed with L and R for the left and right section of the diff viewer, respectively. For example, L-20 means 20th line in the left pane. To highlight a range of line numbers, pass the prefixed line number as an array. For example, [L-2, L-3, L-4, L-5] will highlight the lines 2-5 in the left pane.
showDiffOnly boolean true Shows only the diffed lines and folds the unchanged lines
extraLinesSurroundingDiff number 3 Number of extra unchanged lines surrounding the diff. Works along with showDiffOnly.
codeFoldMessageRenderer function Expand {number} of lines ... Render Prop API to render code fold message.
styles object {} To override style variables and styles. Learn more about overriding styles
useDarkTheme boolean true To enable/disable dark theme.
leftTitle string undefined Column title for left section of the diff in split view. This will be used as the only title in inline view.
rightTitle string undefined Column title for right section of the diff in split view. This will be ignored in inline view.
linesOffset number 0 Number to start count code lines from.

Instance Methods

resetCodeBlocks() - Resets the expanded code blocks to it's initial state. Return true on successful reset and false during unsuccessful reset.

Syntax Highlighting

Syntax highlighting is a bit tricky when combined with diff. Here, React Diff Viewer provides a simple render prop API to handle syntax highlighting. Use renderContent(content: string) => JSX.Element and your favorite syntax highlighting library to achieve this.

An example using Prism JS

// Load Prism CSS
<link
  href="https://cdnjs.cloudflare.com/ajax/libs/prism/1.15.0/prism.min.css"
/>

// Load Prism JS
<script src="https://cdnjs.cloudflare.com/ajax/libs/prism/1.15.0/prism.min.js"></script>
import React, { PureComponent } from 'react';
import ReactDiffViewer from 'react-diff-viewer';

const oldCode = `
const a = 10
const b = 10
const c = () => console.log('foo')

if(a > 10) {
  console.log('bar')
}

console.log('done')
`;
const newCode = `
const a = 10
const boo = 10

if(a === 10) {
  console.log('bar')
}
`;

class Diff extends PureComponent {
  highlightSyntax = (str) => (
    <pre
      style={{ display: 'inline' }}
      dangerouslySetInnerHTML={{
        __html: Prism.highlight(str, Prism.languages.javascript),
      }}
    />
  );

  render = () => {
    return (
      <ReactDiffViewer
        oldValue={oldCode}
        newValue={newCode}
        splitView={true}
        renderContent={this.highlightSyntax}
      />
    );
  };
}

Text block diff comparison

Different styles of text block diffing are possible by using the enums corresponding to variou JsDiff methods (learn more). The supported methods are as follows.

enum DiffMethod {
  CHARS = 'diffChars',
  WORDS = 'diffWords',
  WORDS_WITH_SPACE = 'diffWordsWithSpace',
  LINES = 'diffLines',
  TRIMMED_LINES = 'diffTrimmedLines',
  SENTENCES = 'diffSentences',
  CSS = 'diffCss',
}
import React, { PureComponent } from 'react';
import ReactDiffViewer, { DiffMethod } from 'react-diff-viewer';

const oldCode = `
{
  "name": "Original name",
  "description": null
}
`;
const newCode = `
{
  "name": "My updated name",
  "description": "Brand new description",
  "status": "running"
}
`;

class Diff extends PureComponent {
  render = () => {
    return (
      <ReactDiffViewer
        oldValue={oldCode}
        newValue={newCode}
        compareMethod={DiffMethod.WORDS}
        splitView={true}
      />
    );
  };
}

Overriding Styles

React Diff Viewer uses emotion for styling. It also offers a simple way to override styles and style variables. You can supply different variables for both light and dark themes. Styles will be common for both themes.

Below are the default style variables and style object keys.

// Default variables and style keys

const defaultStyles = {
  variables: {
    light: {
      diffViewerBackground: '#fff',
      diffViewerColor: '#212529',
      addedBackground: '#e6ffed',
      addedColor: '#24292e',
      removedBackground: '#ffeef0',
      removedColor: '#24292e',
      wordAddedBackground: '#acf2bd',
      wordRemovedBackground: '#fdb8c0',
      addedGutterBackground: '#cdffd8',
      removedGutterBackground: '#ffdce0',
      gutterBackground: '#f7f7f7',
      gutterBackgroundDark: '#f3f1f1',
      highlightBackground: '#fffbdd',
      highlightGutterBackground: '#fff5b1',
      codeFoldGutterBackground: '#dbedff',
      codeFoldBackground: '#f1f8ff',
      emptyLineBackground: '#fafbfc',
      gutterColor: '#212529',
      addedGutterColor: '#212529',
      removedGutterColor: '#212529',
      codeFoldContentColor: '#212529',
      diffViewerTitleBackground: '#fafbfc',
      diffViewerTitleColor: '#212529',
      diffViewerTitleBorderColor: '#eee',
    },
    dark: {
      diffViewerBackground: '#2e303c',
      diffViewerColor: '#FFF',
      addedBackground: '#044B53',
      addedColor: 'white',
      removedBackground: '#632F34',
      removedColor: 'white',
      wordAddedBackground: '#055d67',
      wordRemovedBackground: '#7d383f',
      addedGutterBackground: '#034148',
      removedGutterBackground: '#632b30',
      gutterBackground: '#2c2f3a',
      gutterBackgroundDark: '#262933',
      highlightBackground: '#2a3967',
      highlightGutterBackground: '#2d4077',
      codeFoldGutterBackground: '#21232b',
      codeFoldBackground: '#262831',
      emptyLineBackground: '#363946',
      gutterColor: '#464c67',
      addedGutterColor: '#8c8c8c',
      removedGutterColor: '#8c8c8c',
      codeFoldContentColor: '#555a7b',
      diffViewerTitleBackground: '#2f323e',
      diffViewerTitleColor: '#555a7b',
      diffViewerTitleBorderColor: '#353846',
    }
  },
  diffContainer?: {}, // style object
  diffRemoved?: {}, // style object
  diffAdded?: {}, // style object
  marker?: {}, // style object
  emptyGutter?: {}, // style object
  highlightedLine?: {}, // style object
  lineNumber?: {}, // style object
  highlightedGutter?: {}, // style object
  contentText?: {}, // style object
  gutter?: {}, // style object
  line?: {}, // style object
  wordDiff?: {}, // style object
  wordAdded?: {}, // style object
  wordRemoved?: {}, // style object
  codeFoldGutter?: {}, // style object
  codeFold?: {}, // style object
  emptyLine?: {}, // style object
  content?: {}, // style object
  titleBlock?: {}, // style object
  splitView?: {}, // style object
}

To override any style, just pass the new style object to the styles prop. New style will be computed using Object.assign(default, override).

For keys other than variables, the value can either be an object or string interpolation.

import React, { PureComponent } from 'react';
import ReactDiffViewer from 'react-diff-viewer';

const oldCode = `
const a = 10
const b = 10
const c = () => console.log('foo')

if(a > 10) {
  console.log('bar')
}

console.log('done')
`;
const newCode = `
const a = 10
const boo = 10

if(a === 10) {
  console.log('bar')
}
`;

class Diff extends PureComponent {
  highlightSyntax = (str) => (
    <span
      style={{ display: 'inline' }}
      dangerouslySetInnerHTML={{
        __html: Prism.highlight(str, Prism.languages.javascript),
      }}
    />
  );

  render = () => {
    const newStyles = {
      variables: {
        dark: {
          highlightBackground: '#fefed5',
          highlightGutterBackground: '#ffcd3c',
        },
      },
      line: {
        padding: '10px 2px',
        '&:hover': {
          background: '#a26ea1',
        },
      },
    };

    return (
      <ReactDiffViewer
        styles={newStyles}
        oldValue={oldCode}
        newValue={newCode}
        splitView={true}
        renderContent={this.highlightSyntax}
      />
    );
  };
}

Local Development

pnpm install
pnpm build # or use yarn build:watch
pnpm start:examples

Check package.json for more build scripts.

Contributors

Eric M.
Eric M.

💻
Andrei Kovalevsky
Andrei Kovalevsky

💻
Chang Hyun Kim
Chang Hyun Kim

💻

License

MIT

react-diff-viewer-continued's People

Contributors

aeolun avatar allcontributors[bot] avatar amandarice avatar davidfan168 avatar dependabot[bot] avatar dimitropoulos avatar ericmorgan1 avatar fpronto avatar jamiewinder avatar kimbiyam avatar mjesuele avatar orevron avatar praneshr avatar semantic-release-bot avatar seveibar avatar siebeve avatar sogame avatar spyroid avatar sxn avatar xiaoxiaojx avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar

react-diff-viewer-continued's Issues

Improve HTML Semantics of Rendered Diff

This is definitely a useful library, but I would hope for improved HTML semantics.

  • Table caption
    When rendering the table to display the diff, it would be beneficial to support or auto-generate a caption value. In practice this would probably be something like "Changes for [filename]". This way, those navigating with assistive technologies, accessibility trees or landmarks navigation can have greater context as to what the information is.

    https://developer.mozilla.org/en-US/docs/Web/HTML/Element/caption

  • Deletions use <del> tag
    When showing document changes like this diff viewer, and showing deletions, it's more appropriate to use the <del> element. This creates a more accurate semantic experience and doesn't rely entirely on the visual UI to understand what content is being deleted.

    https://developer.mozilla.org/en-US/docs/Web/HTML/Element/del

  • Insertions use <ins> tag
    Much like the previous <del> tag, when displaying insertions, the <ins> tag should be used. Again, this provides greater context to those who may browse with assistive technologies and /or accessibility trees. Using these semantic tags will ensure that the markup communicates as clearly as the UI design.

    https://developer.mozilla.org/en-US/docs/Web/HTML/Element/ins

Default import is not the default export when using ESM

I tried using this in a NextJS ESM project, and the default import is not the default export, rather the module object.

My workaround probably does a better job at explaining the problem:

import diffViewerModule from "react-diff-viewer-continued"
const DiffViewer = diffViewerModule.default

I know that getting typescript and ESM to play nice can be a real PITA, but would it be possible to provide a build for ESM?
Esbuild can handle this, you could perhaps build lib/index.mjs and then put that in the module field in package.json.

suggestion: show a screen shot in the readme

The tagline of this project is, "A simple and beautiful text diff viewer component made with Diff and React."

I think it'd be beneficial to show that beauty right off the bat.

Version 4.0.0 Cannot find module 'react-diff-viewer-continued'

Upgrading to 4.0.0 generates error "TS2307: Cannot find module 'react-diff-viewer-continued' or its corresponding type declarations."

  • in the node_modules folder the lib-folder is missing (is there in previous versions)
  • on npm page the lib-folder is missing (is there in previous versions)

Seems it's shipping without source code.
image

Possible to get the diff summary or delta of line changes?

Would there be any way to get the delta of changes that we could display above or within the diff viewer? Such as number of deletions and number of insertions. We don't want to have to use a separate algorithm to calculate that and get out of sync with what the diff viewer is showing.

renderContent is not able to handle the markdown based content

Issue:

renderContent is not able to render the markdown based content. I am using the MdPreview editor from md-editor-rt. When I am trying to render the markdown based code, its not able to manage the spaces properly and breaking the view by adding many '\n' between lines.

Reference Code

const MarkdownDiffViewer = ({ original, edited }) => {
    return (
      <div>
        <ReactDiffViewer
          newValue={edited}
          oldValue={original}
          splitView={true}
          leftTitle={version}
          rightTitle="latest"
          renderContent={(text) => (
            <MdPreview
              modelValue={text}
              language="en-US"
              previewTheme="github"
            />
          )}
        />
      </div>
    );
  };

<MarkdownDiffViewer
     edited={newcontent}
     original={oldcontent}
 />

Code is completely out of the code block. Is it supported to use markdown rendering using this tool ?

Actual output

image

Diffs in sidebar

Hello,
Is it possible to get sidebar with all content changes. Some demo HERE

Regards, Reinis

JSON Diff viewer not working correctly

if any of the json file is not correct and fails in parsing then its not working and throwing error.
for example if we remove comma from old json and check it will give parse error.

Difference viewer should show all the differences not just parsed json

Add handling of big texts

rn the component is really slow or even crashes when handling large texts.

i use it to compare network devices configurations and one of them is over 11.000 lines but i can't seem bo be able to render it nicely.

would be a nice enhancement adding the handling of such texts

Bug- prism fails if the text in a code is empty line

Description
When the user try to view the diff in split mode, if the old code and new code have, some empty line, prism throws error for syntax highlighting

Step to reproduce

  1. Use the Syntax Highlighting example
  2. Add some empty lines in new code variable, in the beggining (These variables should contain string value)
  3. Set split mode to true

Screenshot of error
image

Screenshot when split mode is false
image

Code sanbox
image
Link
Code sandbox example

Responsive / mobile unified UI

Just some thoughts:

  • Making the UI responsive would allow a setting like view: responsive where it'd switch from split view to unified view depending on the resolution
  • The split view not really being responsive makes sense given the space constraint on mobile devices, tho the unified view could use some CSS love, and maybe an option to hide the line numbers

Fold just can be clicked one time

CodeFold will be removed after first click.
I hope:

  • codeFold can be folded and expanded;
  • developers can customize it show and hide.

Suggestion: Having the option to use custom compare methods

I am currently trying to compare a texts from html documents, and the default compare functions are not working too well for this case. Is it possible to add the option of using custom compare functions? This should also help with other requests like case insensitive diffs: #26

I found a pull request on the original react-diff-viewer which should just allow us to do this:
https://github.com/praneshr/react-diff-viewer/pull/137/files

Will it be ok to add the changes from that commit? If it is then can I just create a pull request for that? I can also update the readme and provide examples on it. (Sorry for all these questions. This is my first time interacting with a public repo)

How to select desired text in splitview?

In splitview, a user will often want either to copy all the text of the left view, or all the text of the right view.
Unfortunately, contrary to e.g. Github diff, when we select text after a paragraph it begins to also select the other side of the splitview, which is undesired.
For example my app needs to show an old version and a new version of a SQL query. The user wants to select chunks or all of the previous query, or of the new one, not a mixed version of both..

How to fix the selection?
The html structure is mixed which seems to prevents a CSS solution for selection.

Syntax highlighting not working

I tried to use the syntax highlighting exactly like the documentation suggests and when i do so (using the same sample code), the lines are getting the right CSS styles applied to each element in the html. however, the styles must be getting overridden somewhere because i see no actual syntax highlighting applied to the rendered content.

Compare method does not seem to be working

Hello,
I've been trying to make the diff-viewer highlight trailing spaces and newlines, but it does not seem to be able to do that.

I tried using different compareMethod values, DiffMethod.WORDS_WITH_SPACE seemed to be the right one. However, it did not do the job.

I also tried separately to use the the method from the diff package, and that managed to return the trailing characters.

Is it something that I am missing? Should I also use it alongside other relevant props?

Cheers!

renderContent highlightSyntax example error

When attempting to add syntax highlighting, I encounter these errors:

Prism.highlight() interface now requires 3 args, not 2:

  • the docs show __html: highlight(str, Prism.languages.javascript), however, it looks like the most recent interface has 3 args:
    highlight-interface
  • easily resolved with __html: Prism.highlight(str, Prism.languages.javascript, 'javascript')

Prism.highlight() arg0 str undefined:

highlight-error-message-arg0-undefined

  • passing a string directly to the highlight function arg0 (e.g. __html: Prism.highlight(oldCode, Prism.languages.javascript, 'javascript') but then this shows the oldCode on both sides of the splitView!

Any advice is appreciated!

packages:

"react": "18.2.0",
"react-diff-viewer-continued": "^3.2.3",
"prismjs": "^1.29.0",
"@types/prismjs": "^1.26.0",

Error using ReactDiffView in Remix App

Summary

I am running into an error when trying to use this library

import ReactDiffViewer from "react-diff-viewer-continued";

export const DiffExpandView = () => {
	return (
		<>
			<ReactDiffViewer
				oldValue={"sourceVersion"}
				newValue={"newVersion"}
				splitView={true}
				// linesOffset={5}
			/>
		</>
	);
}

Here is the error I get:

Error: Element type is invalid: expected a string (for built-in components) or a class/function (for composite components) but got: object.

Context

I am working with a remix-react app.

Any idea how to fix this? This component would really fit my current needs so I would love to be able to use it

diff not on a same line

First image is how it looks on my page and second is how it looks on your website. Why on my page diff is not on the same line
image
image

Render new lines in diff

If i have json in the diff viewer, it shows the json properly, but new lines are rendered as "\n" instead of actual new lines

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.