react-highlight-words
Version:
React component to highlight words within a larger body of text
530 lines (451 loc) • 18.5 kB
JavaScript
module.exports =
/******/ (function(modules) { // webpackBootstrap
/******/ // The module cache
/******/ var installedModules = {};
/******/
/******/ // The require function
/******/ function __webpack_require__(moduleId) {
/******/
/******/ // Check if module is in cache
/******/ if(installedModules[moduleId])
/******/ return installedModules[moduleId].exports;
/******/
/******/ // Create a new module (and put it into the cache)
/******/ var module = installedModules[moduleId] = {
/******/ exports: {},
/******/ id: moduleId,
/******/ loaded: false
/******/ };
/******/
/******/ // Execute the module function
/******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
/******/
/******/ // Flag the module as loaded
/******/ module.loaded = true;
/******/
/******/ // Return the exports of the module
/******/ return module.exports;
/******/ }
/******/
/******/
/******/ // expose the modules object (__webpack_modules__)
/******/ __webpack_require__.m = modules;
/******/
/******/ // expose the module cache
/******/ __webpack_require__.c = installedModules;
/******/
/******/ // __webpack_public_path__
/******/ __webpack_require__.p = "";
/******/
/******/ // Load entry module and return exports
/******/ return __webpack_require__(0);
/******/ })
/************************************************************************/
/******/ ([
/* 0 */
/***/ (function(module, exports, __webpack_require__) {
module.exports = __webpack_require__(1);
/***/ }),
/* 1 */
/***/ (function(module, exports, __webpack_require__) {
'use strict';
Object.defineProperty(exports, '__esModule', {
value: true
});
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
var _Highlighter = __webpack_require__(2);
var _Highlighter2 = _interopRequireDefault(_Highlighter);
exports['default'] = _Highlighter2['default'];
module.exports = exports['default'];
/***/ }),
/* 2 */
/***/ (function(module, exports, __webpack_require__) {
'use strict';
Object.defineProperty(exports, '__esModule', {
value: true
});
var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; };
exports['default'] = Highlighter;
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
function _objectWithoutProperties(obj, keys) { var target = {}; for (var i in obj) { if (keys.indexOf(i) >= 0) continue; if (!Object.prototype.hasOwnProperty.call(obj, i)) continue; target[i] = obj[i]; } return target; }
var _highlightWordsCore = __webpack_require__(3);
var _react = __webpack_require__(4);
var _memoizeOne = __webpack_require__(5);
var _memoizeOne2 = _interopRequireDefault(_memoizeOne);
/**
* Highlighter component
* @param {object} props - Component properties
* @param {string} [props.activeClassName] - The class name to be applied to an active match. Use along with `activeIndex`.
* @param {number} [props.activeIndex] - Specify the match index that should be actively highlighted. Use along with `activeClassName`.
* @param {object} [props.activeStyle] - The inline style to be applied to an active match. Use along with `activeIndex`.
* @param {boolean} [props.autoEscape] - Escape characters in searchWords which are meaningful in regular expressions.
* @param {string} [props.className] - CSS class name applied to the outer/wrapper `<span>`.
* @param {(options: object) => Array<{start: number, end: number}>} [props.findChunks] - Use a custom function to search for matching chunks. See the default `findChunks` function in `highlight-words-core` for signature.
* @param {string|object} [props.highlightClassName] - CSS class name applied to highlighted text or object mapping search term matches to class names.
* @param {object} [props.highlightStyle] - Inline styles applied to highlighted text.
* @param {React.ComponentType|string} [props.highlightTag] - Type of tag to wrap around highlighted matches. Defaults to `mark` but can also be a React component (class or functional).
* @param {(text: string) => string} [props.sanitize] - Process each search word and text to highlight before comparing.
* @param {Array<string|RegExp>} props.searchWords - Array of search words. String search terms are automatically cast to RegExps unless `autoEscape` is true.
* @param {string} props.textToHighlight - The text to highlight matches in.
* @param {React.ComponentType|string} [props.unhighlightTag] - Type of tag applied to unhighlighted parts. Defaults to `span` but can also be a React component (class or functional).
* @param {string} [props.unhighlightClassName] - CSS class name applied to unhighlighted text.
* @param {object} [props.unhighlightStyle] - Inline styles applied to the unhighlighted text.
* @param {object} [props.rest] - Additional attributes passed to the outer `<span>` element.
*/
function Highlighter(_ref) {
var _ref$activeClassName = _ref.activeClassName;
var activeClassName = _ref$activeClassName === undefined ? '' : _ref$activeClassName;
var _ref$activeIndex = _ref.activeIndex;
var activeIndex = _ref$activeIndex === undefined ? -1 : _ref$activeIndex;
var activeStyle = _ref.activeStyle;
var autoEscape = _ref.autoEscape;
var _ref$caseSensitive = _ref.caseSensitive;
var caseSensitive = _ref$caseSensitive === undefined ? false : _ref$caseSensitive;
var className = _ref.className;
var findChunks = _ref.findChunks;
var _ref$highlightClassName = _ref.highlightClassName;
var highlightClassName = _ref$highlightClassName === undefined ? '' : _ref$highlightClassName;
var _ref$highlightStyle = _ref.highlightStyle;
var highlightStyle = _ref$highlightStyle === undefined ? {} : _ref$highlightStyle;
var _ref$highlightTag = _ref.highlightTag;
var highlightTag = _ref$highlightTag === undefined ? 'mark' : _ref$highlightTag;
var sanitize = _ref.sanitize;
var searchWords = _ref.searchWords;
var textToHighlight = _ref.textToHighlight;
var _ref$unhighlightTag = _ref.unhighlightTag;
var unhighlightTag = _ref$unhighlightTag === undefined ? 'span' : _ref$unhighlightTag;
var _ref$unhighlightClassName = _ref.unhighlightClassName;
var unhighlightClassName = _ref$unhighlightClassName === undefined ? '' : _ref$unhighlightClassName;
var unhighlightStyle = _ref.unhighlightStyle;
var rest = _objectWithoutProperties(_ref, ['activeClassName', 'activeIndex', 'activeStyle', 'autoEscape', 'caseSensitive', 'className', 'findChunks', 'highlightClassName', 'highlightStyle', 'highlightTag', 'sanitize', 'searchWords', 'textToHighlight', 'unhighlightTag', 'unhighlightClassName', 'unhighlightStyle']);
var chunks = (0, _highlightWordsCore.findAll)({
autoEscape: autoEscape,
caseSensitive: caseSensitive,
findChunks: findChunks,
sanitize: sanitize,
searchWords: searchWords,
textToHighlight: textToHighlight
});
var HighlightTag = highlightTag;
var highlightIndex = -1;
var highlightClassNames = '';
var highlightStyles = undefined;
var lowercaseProps = function lowercaseProps(object) {
var mapped = {};
for (var key in object) {
mapped[key.toLowerCase()] = object[key];
}
return mapped;
};
var memoizedLowercaseProps = (0, _memoizeOne2['default'])(lowercaseProps);
return (0, _react.createElement)('span', _extends({
className: className
}, rest, {
children: chunks.map(function (chunk, index) {
var text = textToHighlight.substr(chunk.start, chunk.end - chunk.start);
if (chunk.highlight) {
highlightIndex++;
var highlightClass = undefined;
if (typeof highlightClassName === 'object') {
if (!caseSensitive) {
highlightClassName = memoizedLowercaseProps(highlightClassName);
highlightClass = highlightClassName[text.toLowerCase()];
} else {
highlightClass = highlightClassName[text];
}
} else {
highlightClass = highlightClassName;
}
var isActive = highlightIndex === +activeIndex;
highlightClassNames = highlightClass + ' ' + (isActive ? activeClassName : '');
highlightStyles = isActive === true && activeStyle != null ? Object.assign({}, highlightStyle, activeStyle) : highlightStyle;
var props = {
children: text,
className: highlightClassNames,
key: index,
style: highlightStyles
};
// Don't attach arbitrary props to DOM elements; this triggers React DEV warnings (https://fb.me/react-unknown-prop)
// Only pass through the highlightIndex attribute for custom components.
if (typeof HighlightTag !== 'string') {
props.highlightIndex = highlightIndex;
}
return (0, _react.createElement)(HighlightTag, props);
} else {
return (0, _react.createElement)(unhighlightTag, {
children: text,
className: unhighlightClassName,
key: index,
style: unhighlightStyle
});
}
})
}));
}
module.exports = exports['default'];
/***/ }),
/* 3 */
/***/ (function(module, exports) {
module.exports =
/******/ (function(modules) { // webpackBootstrap
/******/ // The module cache
/******/ var installedModules = {};
/******/
/******/ // The require function
/******/ function __webpack_require__(moduleId) {
/******/
/******/ // Check if module is in cache
/******/ if(installedModules[moduleId])
/******/ return installedModules[moduleId].exports;
/******/
/******/ // Create a new module (and put it into the cache)
/******/ var module = installedModules[moduleId] = {
/******/ exports: {},
/******/ id: moduleId,
/******/ loaded: false
/******/ };
/******/
/******/ // Execute the module function
/******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
/******/
/******/ // Flag the module as loaded
/******/ module.loaded = true;
/******/
/******/ // Return the exports of the module
/******/ return module.exports;
/******/ }
/******/
/******/
/******/ // expose the modules object (__webpack_modules__)
/******/ __webpack_require__.m = modules;
/******/
/******/ // expose the module cache
/******/ __webpack_require__.c = installedModules;
/******/
/******/ // __webpack_public_path__
/******/ __webpack_require__.p = "";
/******/
/******/ // Load entry module and return exports
/******/ return __webpack_require__(0);
/******/ })
/************************************************************************/
/******/ ([
/* 0 */
/***/ (function(module, exports, __webpack_require__) {
module.exports = __webpack_require__(1);
/***/ }),
/* 1 */
/***/ (function(module, exports, __webpack_require__) {
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
var _utils = __webpack_require__(2);
Object.defineProperty(exports, 'combineChunks', {
enumerable: true,
get: function get() {
return _utils.combineChunks;
}
});
Object.defineProperty(exports, 'fillInChunks', {
enumerable: true,
get: function get() {
return _utils.fillInChunks;
}
});
Object.defineProperty(exports, 'findAll', {
enumerable: true,
get: function get() {
return _utils.findAll;
}
});
Object.defineProperty(exports, 'findChunks', {
enumerable: true,
get: function get() {
return _utils.findChunks;
}
});
/***/ }),
/* 2 */
/***/ (function(module, exports) {
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
/**
* Creates an array of chunk objects representing both higlightable and non highlightable pieces of text that match each search word.
* @return Array of "chunks" (where a Chunk is { start:number, end:number, highlight:boolean })
*/
var findAll = exports.findAll = function findAll(_ref) {
var autoEscape = _ref.autoEscape,
_ref$caseSensitive = _ref.caseSensitive,
caseSensitive = _ref$caseSensitive === undefined ? false : _ref$caseSensitive,
_ref$findChunks = _ref.findChunks,
findChunks = _ref$findChunks === undefined ? defaultFindChunks : _ref$findChunks,
sanitize = _ref.sanitize,
searchWords = _ref.searchWords,
textToHighlight = _ref.textToHighlight;
return fillInChunks({
chunksToHighlight: combineChunks({
chunks: findChunks({
autoEscape: autoEscape,
caseSensitive: caseSensitive,
sanitize: sanitize,
searchWords: searchWords,
textToHighlight: textToHighlight
})
}),
totalLength: textToHighlight ? textToHighlight.length : 0
});
};
/**
* Takes an array of {start:number, end:number} objects and combines chunks that overlap into single chunks.
* @return {start:number, end:number}[]
*/
var combineChunks = exports.combineChunks = function combineChunks(_ref2) {
var chunks = _ref2.chunks;
chunks = chunks.sort(function (first, second) {
return first.start - second.start;
}).reduce(function (processedChunks, nextChunk) {
// First chunk just goes straight in the array...
if (processedChunks.length === 0) {
return [nextChunk];
} else {
// ... subsequent chunks get checked to see if they overlap...
var prevChunk = processedChunks.pop();
if (nextChunk.start <= prevChunk.end) {
// It may be the case that prevChunk completely surrounds nextChunk, so take the
// largest of the end indeces.
var endIndex = Math.max(prevChunk.end, nextChunk.end);
processedChunks.push({ start: prevChunk.start, end: endIndex });
} else {
processedChunks.push(prevChunk, nextChunk);
}
return processedChunks;
}
}, []);
return chunks;
};
/**
* Examine text for any matches.
* If we find matches, add them to the returned array as a "chunk" object ({start:number, end:number}).
* @return {start:number, end:number}[]
*/
var defaultFindChunks = function defaultFindChunks(_ref3) {
var autoEscape = _ref3.autoEscape,
caseSensitive = _ref3.caseSensitive,
_ref3$sanitize = _ref3.sanitize,
sanitize = _ref3$sanitize === undefined ? identity : _ref3$sanitize,
searchWords = _ref3.searchWords,
textToHighlight = _ref3.textToHighlight;
textToHighlight = sanitize(textToHighlight);
return searchWords.filter(function (searchWord) {
return searchWord;
}) // Remove empty words
.reduce(function (chunks, searchWord) {
searchWord = sanitize(searchWord);
if (autoEscape) {
searchWord = escapeRegExpFn(searchWord);
}
var regex = new RegExp(searchWord, caseSensitive ? 'g' : 'gi');
var match = void 0;
while (match = regex.exec(textToHighlight)) {
var start = match.index;
var end = regex.lastIndex;
// We do not return zero-length matches
if (end > start) {
chunks.push({ start: start, end: end });
}
// Prevent browsers like Firefox from getting stuck in an infinite loop
// See http://www.regexguru.com/2008/04/watch-out-for-zero-length-matches/
if (match.index == regex.lastIndex) {
regex.lastIndex++;
}
}
return chunks;
}, []);
};
// Allow the findChunks to be overridden in findAll,
// but for backwards compatibility we export as the old name
exports.findChunks = defaultFindChunks;
/**
* Given a set of chunks to highlight, create an additional set of chunks
* to represent the bits of text between the highlighted text.
* @param chunksToHighlight {start:number, end:number}[]
* @param totalLength number
* @return {start:number, end:number, highlight:boolean}[]
*/
var fillInChunks = exports.fillInChunks = function fillInChunks(_ref4) {
var chunksToHighlight = _ref4.chunksToHighlight,
totalLength = _ref4.totalLength;
var allChunks = [];
var append = function append(start, end, highlight) {
if (end - start > 0) {
allChunks.push({
start: start,
end: end,
highlight: highlight
});
}
};
if (chunksToHighlight.length === 0) {
append(0, totalLength, false);
} else {
var lastIndex = 0;
chunksToHighlight.forEach(function (chunk) {
append(lastIndex, chunk.start, false);
append(chunk.start, chunk.end, true);
lastIndex = chunk.end;
});
append(lastIndex, totalLength, false);
}
return allChunks;
};
function identity(value) {
return value;
}
function escapeRegExpFn(str) {
return str.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, '\\$&');
}
/***/ })
/******/ ]);
//# sourceMappingURL=index.js.map
/***/ }),
/* 4 */
/***/ (function(module, exports) {
module.exports = require("react");
/***/ }),
/* 5 */
/***/ (function(module, exports) {
'use strict';
var simpleIsEqual = function simpleIsEqual(a, b) {
return a === b;
};
function index (resultFn) {
var isEqual = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : simpleIsEqual;
var lastThis = void 0;
var lastArgs = [];
var lastResult = void 0;
var calledOnce = false;
var isNewArgEqualToLast = function isNewArgEqualToLast(newArg, index) {
return isEqual(newArg, lastArgs[index]);
};
var result = function result() {
for (var _len = arguments.length, newArgs = Array(_len), _key = 0; _key < _len; _key++) {
newArgs[_key] = arguments[_key];
}
if (calledOnce && lastThis === this && newArgs.length === lastArgs.length && newArgs.every(isNewArgEqualToLast)) {
return lastResult;
}
calledOnce = true;
lastThis = this;
lastArgs = newArgs;
lastResult = resultFn.apply(this, newArgs);
return lastResult;
};
return result;
}
module.exports = index;
/***/ })
/******/ ]);
//# sourceMappingURL=main.js.map