diff --git a/README.md b/README.md index 9c067e7..123a6ef 100644 --- a/README.md +++ b/README.md @@ -43,13 +43,14 @@ const SimpleCloud = () => ( | Option | Type | Required | Note | |-----------|----------|--------|---| -|`tags` |`Array` |`true`|Array of objects representing tags (see [Tag object](#tag-object))| -|`maxSize` |`Number` |`true` |Maximal font size (px) used in cloud| -|`minSize` |`Number` |`true` |Minimal font size (px) used in cloud| -|`shuffle` |`Boolean` |`false`|If true, tags are shuffled. When `tags` are modified, cloud is re-shuffled. Default: `true`| -|`colorOptions` |`Object` |`false`|Random color options (see [randomColor#options](https://github.com/davidmerfield/randomColor#options))| -|`disableRandomColor`|`Boolean` |`false`|If `true`, random color is not used| -|`renderer` |`Function`|`false`|Function used to render each tag| +|`tags` |`Array` |`true`|Array of objects representing tags (see [Tag object](#tag-object))| +|`maxSize` |`Number` |`true` |Maximal font size (px) used in cloud| +|`minSize` |`Number` |`true` |Minimal font size (px) used in cloud| +|`shuffle` |`Boolean` |`false`|If true, tags are shuffled. When `tags` are modified, cloud is re-shuffled. Default: `true`| +|`randomNumberGenerator`|`Function`|`false`|Specifies a custom random number generator that is being used by shuffle algorithm. Default: `Math.random` +|`colorOptions` |`Object` |`false`|Random color options (see [randomColor#options](https://github.com/davidmerfield/randomColor#options))| +|`disableRandomColor` |`Boolean` |`false`|If `true`, random color is not used| +|`renderer` |`Function`|`false`|Function used to render each tag| *Note:* Furthermore you can pass any other option and it will be passed forward to the wrapping `
` component (e.g. `style`, `className`). diff --git a/__tests__/TagCloud-test.js b/__tests__/TagCloud-test.js index 9b716b1..966d08d 100644 --- a/__tests__/TagCloud-test.js +++ b/__tests__/TagCloud-test.js @@ -2,7 +2,10 @@ jest.unmock('../src/TagCloud'); jest.unmock('../src/defaultRenderer'); jest.unmock('../src/helpers'); -jest.mock('array-shuffle', () => arr => arr.slice().reverse()); +jest.mock('shuffle-array', () => (arr, opts = {}) => { + opts.rng && opts.rng(); + return arr.slice().reverse(); +}); import React from 'react'; import ReactDOM from 'react-dom'; @@ -120,4 +123,12 @@ describe('TagCloud', () => { cloud._data.forEach((t, i) => expect(t.tag).toEqual(data[i])); }); + it('should use custom rng', () => { + const rng = jest.fn(); + TestUtils.renderIntoDocument( + + ); + + expect(rng).toHaveBeenCalled(); + }); }); diff --git a/examples/src/App.js b/examples/src/App.js index d464157..6682e94 100644 --- a/examples/src/App.js +++ b/examples/src/App.js @@ -8,7 +8,8 @@ const examples = [ {file: 'simple-cloud.js', title: 'Simple cloud', key: 1}, {file: 'custom-color-options.js', title: 'Custom color options', key: 2}, {file: 'custom-styles.js', title: 'Custom styles', key: 3}, - {file: 'custom-renderer.js', title: 'Custom renderer', key: 4} + {file: 'custom-renderer.js', title: 'Custom renderer', key: 4}, + {file: 'shuffle-with-seed.js', title: 'Shuffle with seed', key: 5} ]; class App extends React.Component { diff --git a/examples/src/shuffle-with-seed.js b/examples/src/shuffle-with-seed.js new file mode 100644 index 0000000..63545f2 --- /dev/null +++ b/examples/src/shuffle-with-seed.js @@ -0,0 +1,29 @@ +import React from "react"; +import { TagCloud } from "react-tagcloud"; + +const data = [ + { value: "jQuery", count: 25 }, { value: "MongoDB", count: 18 }, + { value: "JavaScript", count: 38 }, { value: "React", count: 30 }, + { value: "Nodejs", count: 28 }, { value: "Express.js", count: 25 }, + { value: "HTML5", count: 33 }, { value: "CSS3", count: 20 }, + { value: "Webpack", count: 22 }, { value: "Babel.js", count: 7 }, + { value: "ECMAScript", count: 25 }, { value: "Jest", count: 15 }, + { value: "Mocha", count: 17 }, { value: "React Native", count: 27 }, + { value: "Angular.js", count: 30 }, { value: "TypeScript", count: 15 }, + { value: "Flow", count: 30 }, { value: "NPM", count: 11 }, +]; + +// bring your own implementation of rng +let seed = 1337; +function random() { + const x = Math.sin(seed++) * 10000; + return x - Math.floor(x); +} + +export default () => ( + +); + diff --git a/lib/TagCloud.js b/lib/TagCloud.js index 7f47300..adab68e 100644 --- a/lib/TagCloud.js +++ b/lib/TagCloud.js @@ -17,9 +17,9 @@ var _propTypes2 = _interopRequireDefault(_propTypes); var _defaultRenderer = require('./defaultRenderer'); -var _arrayShuffle = require('array-shuffle'); +var _shuffleArray = require('shuffle-array'); -var _arrayShuffle2 = _interopRequireDefault(_arrayShuffle); +var _shuffleArray2 = _interopRequireDefault(_shuffleArray); var _randomcolor = require('randomcolor'); @@ -38,7 +38,7 @@ function _possibleConstructorReturn(self, call) { if (!self) { throw new Referen 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 eventHandlers = ['onClick', 'onDoubleClick', 'onMouseMove']; -var cloudProps = ['tags', 'shuffle', 'renderer', 'maxSize', 'minSize', 'colorOptions', 'disableRandomColor']; +var cloudProps = ['tags', 'shuffle', 'renderer', 'maxSize', 'minSize', 'colorOptions', 'disableRandomColor', 'randomNumberGenerator']; var generateColor = function generateColor(tag, _ref) { var disableRandomColor = _ref.disableRandomColor, @@ -116,7 +116,8 @@ var TagCloud = exports.TagCloud = function (_React$Component) { var tags = props.tags, shuffle = props.shuffle, minSize = props.minSize, - maxSize = props.maxSize; + maxSize = props.maxSize, + randomNumberGenerator = props.randomNumberGenerator; var counts = tags.map(function (tag) { return tag.count; @@ -130,7 +131,7 @@ var TagCloud = exports.TagCloud = function (_React$Component) { fontSize: (0, _helpers.fontSizeConverter)(tag.count, min, max, minSize, maxSize) }; }); - this._data = shuffle ? (0, _arrayShuffle2.default)(data) : data; + this._data = shuffle ? (0, _shuffleArray2.default)(data, { copy: true, rng: randomNumberGenerator }) : data; } }]); @@ -145,7 +146,8 @@ TagCloud.propTypes = { colorOptions: _propTypes2.default.object, disableRandomColor: _propTypes2.default.bool, renderer: _propTypes2.default.func, - className: _propTypes2.default.string + className: _propTypes2.default.string, + randomNumberGenerator: _propTypes2.default.func }; TagCloud.defaultProps = { diff --git a/lib/tag-cloud.js b/lib/tag-cloud.js index f5a98b8..b7df357 100644 --- a/lib/tag-cloud.js +++ b/lib/tag-cloud.js @@ -24,7 +24,7 @@ var _defaultRenderer = require("./default-renderer"); var _defaultRenderer2 = _interopRequireDefault(_defaultRenderer); -var _arrayShuffle = require("array-shuffle"); +var _arrayShuffle = require("shuffle-array"); var _arrayShuffle2 = _interopRequireDefault(_arrayShuffle); @@ -105,4 +105,4 @@ TagCloud.defaultProps = { shuffle: true, className: "tag-cloud" }; -module.exports = exports["default"]; \ No newline at end of file +module.exports = exports["default"]; diff --git a/package.json b/package.json index cefd9ab..55734c1 100644 --- a/package.json +++ b/package.json @@ -29,10 +29,10 @@ "collectCoverage": true }, "dependencies": { - "array-shuffle": "^1.0.0", "object-assign": "^4.1.0", "prop-types": "^15.6.2", - "randomcolor": "^0.4.2" + "randomcolor": "^0.4.2", + "shuffle-array": "^1.0.1" }, "peerDependencies": { "react": "^16.0.0" diff --git a/src/TagCloud.js b/src/TagCloud.js index 934025a..756497c 100644 --- a/src/TagCloud.js +++ b/src/TagCloud.js @@ -1,12 +1,12 @@ import React from 'react'; import PropTypes from 'prop-types'; import { defaultRenderer } from './defaultRenderer'; -import arrayShuffle from 'array-shuffle'; +import arrayShuffle from 'shuffle-array'; import randomColor from 'randomcolor'; import { omitProps, includeProps, fontSizeConverter, arraysEqual, propertiesEqual } from './helpers'; const eventHandlers = ['onClick', 'onDoubleClick', 'onMouseMove']; -const cloudProps = ['tags', 'shuffle', 'renderer', 'maxSize', 'minSize', 'colorOptions', 'disableRandomColor']; +const cloudProps = ['tags', 'shuffle', 'renderer', 'maxSize', 'minSize', 'colorOptions', 'disableRandomColor', 'randomNumberGenerator']; const generateColor = (tag, {disableRandomColor, colorOptions}) => { if (tag.color) { @@ -59,7 +59,7 @@ export class TagCloud extends React.Component { } _populate(props) { - const { tags, shuffle, minSize, maxSize } = props; + const { tags, shuffle, minSize, maxSize, randomNumberGenerator } = props; const counts = tags.map(tag => tag.count), min = Math.min(...counts), max = Math.max(...counts); @@ -68,7 +68,7 @@ export class TagCloud extends React.Component { color: generateColor(tag, props), fontSize: fontSizeConverter(tag.count, min, max, minSize, maxSize) })); - this._data = shuffle ? arrayShuffle(data) : data; + this._data = shuffle ? arrayShuffle(data, { copy: true, rng: randomNumberGenerator }) : data; } } @@ -81,7 +81,8 @@ TagCloud.propTypes = { colorOptions: PropTypes.object, disableRandomColor: PropTypes.bool, renderer: PropTypes.func, - className: PropTypes.string + className: PropTypes.string, + randomNumberGenerator: PropTypes.func, }; TagCloud.defaultProps = {