From 588d29e5f3bae8b36e3a73010c3686e3a9427382 Mon Sep 17 00:00:00 2001 From: David Mason Date: Fri, 27 Nov 2015 12:15:27 +1000 Subject: [PATCH 1/3] feat: change to output tags instead of inline-styled elements I decided on a separate release for this since it loses functionality from the react-diff project (no longer displays with pink/green styles when first installed). The use-case here is a little different - a bland but stylable diff that requires a little work to make it look ok, whereas the other is more of a "just show me a diff with minimal effort" thing. --- README.md | 77 ++++++++++++++++++++++++++++++++++++++++++---- dist/react-diff.js | 55 ++++++++++++++++++++++++--------- lib/react-diff.js | 32 ++++++++++++++----- package.json | 20 +++++++----- 4 files changed, 149 insertions(+), 35 deletions(-) diff --git a/README.md b/README.md index 26a3afe..1bb6043 100644 --- a/README.md +++ b/README.md @@ -1,27 +1,92 @@ # react-diff -Highlights differences between two strings, uses the [diff](https://www.npmjs.com/package/diff) module +Output differences between two strings in a stylable form. + +Based on [react-diff](https://www.npmjs.com/package/react-diff). Uses the [diff](https://www.npmjs.com/package/diff) module ## Installation ``` -npm install react-diff +npm install react-stylable-diff +``` + +## Usage + +Pass text to compare as `props.inputA` and `props.inputB`: + +```javascript +var React = require('react'); +var Diff = require('react-stylable-diff'); + +var MyComponent = React.createClass({ + render: function() { + return ( + + ); + } +}); +``` + +You can also specify different values in `props.type` +to compare in different ways. Valid values are `'chars'`, +`'words'`, `'sentences'` and `'json'`: + + +```javascript +var React = require('react'); +var Diff = require('react-stylable-diff'); + +var MyComponent = React.createClass({ + render: function() { + return ( + + ); + } +}); ``` -## Demo +### Styling + +Outputs standard `` and `` tags so you will at least +have the browser default styling for these. On my browser they +appear crossed-out or underlined. + +You will probably want to add your own styles to look all fancy. + +The output is wrapped in a div with class `'Difference'` so you can +attach all your style rules to that. You can override this class with +`props.className` if you like. -http://cezary.github.io/react-diff/ +Here are some styles that might work: + +```css +.Difference { + font-family: monospace; +} + +.Difference > del { + background-color: rgb(255, 224, 224); + text-decoration: none; +} + +.Difference > ins { + background-color: rgb(201, 238, 211); + text-decoration: none; +} +``` ## Example ```javascript var React = require('react'); -var Diff = require('react-diff'); +var Diff = require('react-stylable-diff'); var Component = React.createClass({ render: function() { return ( - + ); } }); diff --git a/dist/react-diff.js b/dist/react-diff.js index 947b6db..6af1406 100644 --- a/dist/react-diff.js +++ b/dist/react-diff.js @@ -4,12 +4,23 @@ var React = require('react'); var jsdiff = require('diff'); var fnMap = { - chars: jsdiff.diffChars, - words: jsdiff.diffWords, - sentences: jsdiff.diffSentences, - json: jsdiff.diffJson + 'chars': jsdiff.diffChars, + 'words': jsdiff.diffWords, + 'sentences': jsdiff.diffSentences, + 'json': jsdiff.diffJson }; +/** + * Display diff in a stylable form. + * + * Default is character diff. Change with props.type. Valid values + * are 'chars', 'words', 'sentences', 'json'. + * + * - Wrapping div has class 'Difference', override with props.className + * - added parts are in + * - removed parts are in + * - unchanged parts are in + */ module.exports = React.createClass({ displayName: 'Diff', @@ -17,32 +28,48 @@ module.exports = React.createClass({ return { inputA: '', inputB: '', - type: 'chars' + type: 'chars', + className: 'Difference' }; }, propTypes: { inputA: React.PropTypes.oneOfType([React.PropTypes.string, React.PropTypes.object]), inputB: React.PropTypes.oneOfType([React.PropTypes.string, React.PropTypes.object]), - type: React.PropTypes.oneOf(['chars', 'words', 'sentences', 'json']) + type: React.PropTypes.oneOf(['chars', 'words', 'sentences', 'json']), + className: React.PropTypes.string }, render: function render() { var diff = fnMap[this.props.type](this.props.inputA, this.props.inputB); - var result = diff.map(function (part) { - var spanStyle = { - backgroundColor: part.added ? 'lightgreen' : part.removed ? 'salmon' : 'lightgrey' - }; + + var result = diff.map(function (part, index) { + if (part.added) { + return React.createElement( + 'ins', + { key: index }, + part.value + ); + } + if (part.removed) { + return React.createElement( + 'del', + { key: index }, + part.value + ); + } return React.createElement( 'span', - { style: spanStyle }, + { key: index }, part.value ); }); + return React.createElement( - 'pre', - { className: 'diff-result' }, + 'div', + { className: this.props.className }, result ); - } }); + } +}); diff --git a/lib/react-diff.js b/lib/react-diff.js index 24dad04..6fd6bf4 100644 --- a/lib/react-diff.js +++ b/lib/react-diff.js @@ -8,6 +8,17 @@ var fnMap = { 'json': jsdiff.diffJson }; +/** + * Display diff in a stylable form. + * + * Default is character diff. Change with props.type. Valid values + * are 'chars', 'words', 'sentences', 'json'. + * + * - Wrapping div has class 'Difference', override with props.className + * - added parts are in + * - removed parts are in + * - unchanged parts are in + */ module.exports = React.createClass({ displayName: 'Diff', @@ -15,7 +26,8 @@ module.exports = React.createClass({ return { inputA: '', inputB: '', - type: 'chars' + type: 'chars', + className: 'Difference' }; }, @@ -33,21 +45,27 @@ module.exports = React.createClass({ 'words', 'sentences', 'json' - ]) + ]), + className: React.PropTypes.string }, render: function () { var diff = fnMap[this.props.type](this.props.inputA, this.props.inputB); + var result = diff.map(function(part, index) { - var spanStyle = { - backgroundColor: part.added ? 'lightgreen' : part.removed ? 'salmon' : 'lightgrey' + if (part.added) { + return {part.value} } - return {part.value} + if (part.removed) { + return {part.value} + } + return {part.value} }); + return ( -
+      
{result} -
+ ); }, }); diff --git a/package.json b/package.json index 80d8ade..7bd637e 100644 --- a/package.json +++ b/package.json @@ -1,25 +1,29 @@ { - "name": "react-diff", - "version": "0.0.6", - "description": "Highlight differences between inputs", + "name": "react-stylable-diff", + "version": "1.0.0", + "description": "Output differences between inputs, ready for css styling", "main": "dist/react-diff.js", "scripts": { "build": "./node_modules/.bin/babel lib/react-diff.js > dist/react-diff.js" }, "repository": { "type": "git", - "url": "https://github.com/cezary/react-diff.git" + "url": "https://github.com/davidmason/react-stylable-diff.git" }, "keywords": [ "react", - "diff" + "diff", + "css" + ], + "author": "David Mason ", + "contributors": [ + "Cezary Wojtkowski " ], - "author": "Cezary Wojtkowski ", "license": "MIT", "bugs": { - "url": "https://github.com/cezary/react-diff/issues" + "url": "https://github.com/davidmason/react-stylable-diff/issues" }, - "homepage": "https://github.com/cezary/react-diff", + "homepage": "https://github.com/davidmason/react-stylable-diff", "peerDependencies": { "react": ">=0.12.0" }, From 195a0de00ca21485bd67492e374de06ad07250ca Mon Sep 17 00:00:00 2001 From: David Mason Date: Fri, 27 Nov 2015 12:22:28 +1000 Subject: [PATCH 2/3] docs: fix title in README --- README.md | 2 +- package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 1bb6043..3a3f9da 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# react-diff +# react-stylable-diff Output differences between two strings in a stylable form. diff --git a/package.json b/package.json index 7bd637e..e1c0a69 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "react-stylable-diff", - "version": "1.0.0", + "version": "1.0.1", "description": "Output differences between inputs, ready for css styling", "main": "dist/react-diff.js", "scripts": { From d98efefcaf1c9d3b1ff975e4b3907861a7d0f46b Mon Sep 17 00:00:00 2001 From: Felix Hao Date: Sat, 6 Feb 2016 10:36:00 +0800 Subject: [PATCH 3/3] add options, more compare types and es6 --- .babelrc | 3 ++ README.md | 10 ++-- dist/react-diff.js | 132 +++++++++++++++++++++++++++++---------------- lib/react-diff.js | 71 ------------------------ lib/react-diff.jsx | 81 ++++++++++++++++++++++++++++ package.json | 11 ++-- 6 files changed, 181 insertions(+), 127 deletions(-) create mode 100644 .babelrc delete mode 100644 lib/react-diff.js create mode 100644 lib/react-diff.jsx diff --git a/.babelrc b/.babelrc new file mode 100644 index 0000000..fe8cd1d --- /dev/null +++ b/.babelrc @@ -0,0 +1,3 @@ +{ + "presets": ["es2015", "react"] +} \ No newline at end of file diff --git a/README.md b/README.md index 3a3f9da..7963c35 100644 --- a/README.md +++ b/README.md @@ -28,8 +28,7 @@ var MyComponent = React.createClass({ ``` You can also specify different values in `props.type` -to compare in different ways. Valid values are `'chars'`, -`'words'`, `'sentences'` and `'json'`: +to compare in different ways. Valid values are `'chars'`, `'words'`, `'wordsWit Space'`, `'lines'`, `'trimmedLines'`, `'sentences'`, `'css'`, and `'json'`. You can also use options (check it in [diff](https://github.com/kpdecker/jsdiff) module): ```javascript @@ -39,9 +38,10 @@ var Diff = require('react-stylable-diff'); var MyComponent = React.createClass({ render: function() { return ( - + ); } }); diff --git a/dist/react-diff.js b/dist/react-diff.js index 6af1406..c57bf92 100644 --- a/dist/react-diff.js +++ b/dist/react-diff.js @@ -1,13 +1,34 @@ 'use strict'; -var React = require('react'); -var jsdiff = require('diff'); +var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); + +Object.defineProperty(exports, "__esModule", { + value: true +}); + +var _react = require('react'); + +var _react2 = _interopRequireDefault(_react); + +var _diff = require('diff'); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } + +function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; } + +function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } var fnMap = { - 'chars': jsdiff.diffChars, - 'words': jsdiff.diffWords, - 'sentences': jsdiff.diffSentences, - 'json': jsdiff.diffJson + 'chars': _diff.diffChars, + 'words': _diff.diffWords, + 'wordsWithSpace': _diff.diffWrodsWithSpace, + 'lines': _diff.diffLines, + 'trimmedLines': _diff.diffTrimmedLines, + 'sentences': _diff.diffSentences, + 'css': _diff.diffCss, + 'json': _diff.diffJson }; /** @@ -21,55 +42,72 @@ var fnMap = { * - removed parts are in * - unchanged parts are in */ -module.exports = React.createClass({ - displayName: 'Diff', - getDefaultProps: function getDefaultProps() { - return { +var ReactDiff = function (_React$Component) { + _inherits(ReactDiff, _React$Component); + + function ReactDiff() { + _classCallCheck(this, ReactDiff); + + var _this = _possibleConstructorReturn(this, Object.getPrototypeOf(ReactDiff).call(this)); + + _this.displayName = 'Diff'; + + _this.defaultProps = { inputA: '', inputB: '', type: 'chars', - className: 'Difference' + options: null, + className: 'difference' }; - }, - - propTypes: { - inputA: React.PropTypes.oneOfType([React.PropTypes.string, React.PropTypes.object]), - inputB: React.PropTypes.oneOfType([React.PropTypes.string, React.PropTypes.object]), - type: React.PropTypes.oneOf(['chars', 'words', 'sentences', 'json']), - className: React.PropTypes.string - }, - - render: function render() { - var diff = fnMap[this.props.type](this.props.inputA, this.props.inputB); - - var result = diff.map(function (part, index) { - if (part.added) { - return React.createElement( - 'ins', - { key: index }, - part.value - ); - } - if (part.removed) { - return React.createElement( - 'del', + + _this.propTypes = { + inputA: _react2.default.PropTypes.oneOfType([_react2.default.PropTypes.string, _react2.default.PropTypes.object]), + inputB: _react2.default.PropTypes.oneOfType([_react2.default.PropTypes.string, _react2.default.PropTypes.object]), + type: _react2.default.PropTypes.oneOf(['chars', 'words', 'wordsWithSpace', 'lines', 'trimmedLines', 'sentences', 'css', 'json']), + options: _react2.default.PropTypes.object + }; + return _this; + } + + _createClass(ReactDiff, [{ + key: 'render', + value: function render() { + var diff = fnMap[this.props.type](this.props.inputA, this.props.inputB, this.props.options); + + var result = diff.map(function (part, index) { + if (part.added) { + return _react2.default.createElement( + 'ins', + { key: index }, + part.value + ); + } + if (part.removed) { + return _react2.default.createElement( + 'del', + { key: index }, + part.value + ); + } + return _react2.default.createElement( + 'span', { key: index }, part.value ); - } - return React.createElement( - 'span', - { key: index }, - part.value + }); + + return _react2.default.createElement( + 'div', + this.props, + result ); - }); + } + }]); - return React.createElement( - 'div', - { className: this.props.className }, - result - ); - } -}); + return ReactDiff; +}(_react2.default.Component); + +exports.default = ReactDiff; +; diff --git a/lib/react-diff.js b/lib/react-diff.js deleted file mode 100644 index 6fd6bf4..0000000 --- a/lib/react-diff.js +++ /dev/null @@ -1,71 +0,0 @@ -var React = require('react'); -var jsdiff = require('diff'); - -var fnMap = { - 'chars': jsdiff.diffChars, - 'words': jsdiff.diffWords, - 'sentences': jsdiff.diffSentences, - 'json': jsdiff.diffJson -}; - -/** - * Display diff in a stylable form. - * - * Default is character diff. Change with props.type. Valid values - * are 'chars', 'words', 'sentences', 'json'. - * - * - Wrapping div has class 'Difference', override with props.className - * - added parts are in - * - removed parts are in - * - unchanged parts are in - */ -module.exports = React.createClass({ - displayName: 'Diff', - - getDefaultProps: function() { - return { - inputA: '', - inputB: '', - type: 'chars', - className: 'Difference' - }; - }, - - propTypes: { - inputA: React.PropTypes.oneOfType([ - React.PropTypes.string, - React.PropTypes.object - ]), - inputB: React.PropTypes.oneOfType([ - React.PropTypes.string, - React.PropTypes.object - ]), - type: React.PropTypes.oneOf([ - 'chars', - 'words', - 'sentences', - 'json' - ]), - className: React.PropTypes.string - }, - - render: function () { - var diff = fnMap[this.props.type](this.props.inputA, this.props.inputB); - - var result = diff.map(function(part, index) { - if (part.added) { - return {part.value} - } - if (part.removed) { - return {part.value} - } - return {part.value} - }); - - return ( -
- {result} -
- ); - }, -}); diff --git a/lib/react-diff.jsx b/lib/react-diff.jsx new file mode 100644 index 0000000..858799e --- /dev/null +++ b/lib/react-diff.jsx @@ -0,0 +1,81 @@ +import React from 'react'; +import {diffChars, diffWords, diffWrodsWithSpace, diffLines, diffTrimmedLines, diffSentences, diffCss, diffJson} from 'diff'; + +var fnMap = { + 'chars': diffChars, + 'words': diffWords, + 'wordsWithSpace': diffWrodsWithSpace, + 'lines': diffLines, + 'trimmedLines': diffTrimmedLines, + 'sentences': diffSentences, + 'css': diffCss, + 'json': diffJson +}; + +/** + * Display diff in a stylable form. + * + * Default is character diff. Change with props.type. Valid values + * are 'chars', 'words', 'sentences', 'json'. + * + * - Wrapping div has class 'Difference', override with props.className + * - added parts are in + * - removed parts are in + * - unchanged parts are in + */ +export default class ReactDiff extends React.Component { + constructor() { + super(); + this.displayName = 'Diff'; + + this.defaultProps = { + inputA: '', + inputB: '', + type: 'chars', + options: null, + className: 'difference' + }; + + this.propTypes = { + inputA: React.PropTypes.oneOfType([ + React.PropTypes.string, + React.PropTypes.object + ]), + inputB: React.PropTypes.oneOfType([ + React.PropTypes.string, + React.PropTypes.object + ]), + type: React.PropTypes.oneOf([ + 'chars', + 'words', + 'wordsWithSpace', + 'lines', + 'trimmedLines', + 'sentences', + 'css', + 'json' + ]), + options: React.PropTypes.object + } + } + + render() { + var diff = fnMap[this.props.type](this.props.inputA, this.props.inputB, this.props.options); + + var result = diff.map(function(part, index) { + if (part.added) { + return {part.value}; + } + if (part.removed) { + return {part.value}; + } + return {part.value}; + }); + + return ( +
+ {result} +
+ ); + } +}; diff --git a/package.json b/package.json index e1c0a69..18bc6d5 100644 --- a/package.json +++ b/package.json @@ -4,7 +4,7 @@ "description": "Output differences between inputs, ready for css styling", "main": "dist/react-diff.js", "scripts": { - "build": "./node_modules/.bin/babel lib/react-diff.js > dist/react-diff.js" + "build": "babel lib --out-dir dist" }, "repository": { "type": "git", @@ -17,7 +17,8 @@ ], "author": "David Mason ", "contributors": [ - "Cezary Wojtkowski " + "Cezary Wojtkowski ", + "Felix Hao " ], "license": "MIT", "bugs": { @@ -28,9 +29,11 @@ "react": ">=0.12.0" }, "dependencies": { - "diff": "^1.2.0" + "diff": "^2.2.1" }, "devDependencies": { - "babel": "^5.1.10" + "babel": "^6.3.26", + "babel-preset-es2015": "^6.3.13", + "babel-preset-react": "^6.3.13" } }