diff --git a/CHANGELOG.md b/CHANGELOG.md
index 82abcf003a5..56b17fc6072 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,6 @@
## [`master`](https://github.com/elastic/eui/tree/master)
+- Added `highlightAll` prop to `EuiHighlight` to highlight all matches ([#2957](https://github.com/elastic/eui/pull/2957))
- Added `showOnFocus` prop to `EuiScreenReaderOnly` to force display on keyboard focus ([#2976](https://github.com/elastic/eui/pull/2976))
- Added `EuiSkipLink` component ([#2976](https://github.com/elastic/eui/pull/2976))
- Created `EuiBadgeGroup` component ([#2921](https://github.com/elastic/eui/pull/2921))
diff --git a/src-docs/src/views/highlight/highlight.js b/src-docs/src/views/highlight/highlight.js
index fcebbe155d0..0406f5daf92 100644
--- a/src-docs/src/views/highlight/highlight.js
+++ b/src-docs/src/views/highlight/highlight.js
@@ -4,6 +4,7 @@ import {
EuiHighlight,
EuiFieldSearch,
EuiSpacer,
+ EuiSwitch,
} from '../../../../src/components';
export class Highlight extends Component {
@@ -12,6 +13,7 @@ export class Highlight extends Component {
this.state = {
searchValue: 'jumped over',
+ isHighlightAll: false,
};
}
@@ -22,15 +24,24 @@ export class Highlight extends Component {
});
};
+ setHighlightAll = e => {
+ this.setState({ isHighlightAll: e.target.checked });
+ };
+
render() {
- const { searchValue } = this.state;
+ const { searchValue, isHighlightAll } = this.state;
return (
-
-
+ this.setHighlightAll(e)}
+ />
+
+
The quick brown fox jumped over the lazy dog
diff --git a/src-docs/src/views/highlight/highlight_example.js b/src-docs/src/views/highlight/highlight_example.js
index c22ae02fd41..87ebc10f4c7 100644
--- a/src-docs/src/views/highlight/highlight_example.js
+++ b/src-docs/src/views/highlight/highlight_example.js
@@ -31,6 +31,7 @@ export const HighlightExample = {
string, typically in response to user input.
),
+ props: { EuiHighlight },
components: { EuiHighlight },
demo: ,
},
diff --git a/src/components/highlight/highlight.tsx b/src/components/highlight/highlight.tsx
index 645d9691586..18fa7b408f7 100644
--- a/src/components/highlight/highlight.tsx
+++ b/src/components/highlight/highlight.tsx
@@ -1,6 +1,21 @@
import React, { Fragment, HTMLAttributes, FunctionComponent } from 'react';
import { CommonProps } from '../common';
+interface EuiHighlightChunk {
+ /**
+ * Start of the chunk
+ */
+ start: number;
+ /**
+ * End of the chunk
+ */
+ end: number;
+ /**
+ * Whether to highlight chunk or not
+ */
+ highlight?: boolean;
+}
+
export type EuiHighlightProps = HTMLAttributes &
CommonProps & {
children: string;
@@ -14,12 +29,18 @@ export type EuiHighlightProps = HTMLAttributes &
* Should the search be strict or not
*/
strict?: boolean;
+
+ /**
+ * Should highlight all matches
+ */
+ highlightAll?: boolean;
};
const highlight = (
searchSubject: string,
searchValue: string,
- isStrict: boolean = false
+ isStrict: boolean,
+ highlightAll: boolean
) => {
if (!searchValue) {
return searchSubject;
@@ -29,6 +50,22 @@ const highlight = (
return null;
}
+ if (highlightAll) {
+ const chunks = getHightlightWords(searchSubject, searchValue, isStrict);
+ return (
+
+ {chunks.map(chunk => {
+ const { end, highlight, start } = chunk;
+ const value = searchSubject.substr(start, end - start);
+ if (highlight) {
+ return {value};
+ }
+ return value;
+ })}
+
+ );
+ }
+
const normalizedSearchSubject: string = isStrict
? searchSubject
: searchSubject.toLowerCase();
@@ -58,16 +95,62 @@ const highlight = (
);
};
+const getHightlightWords = (
+ searchSubject: string,
+ searchValue: string,
+ isStrict: boolean
+) => {
+ const regex = new RegExp(searchValue, isStrict ? 'g' : 'gi');
+ const matches = [];
+ let match;
+ while ((match = regex.exec(searchSubject)) !== null) {
+ matches.push({
+ start: match.index,
+ end: (match.index || 0) + match[0].length,
+ });
+ }
+ return fillInChunks(matches, searchSubject.length);
+};
+
+const fillInChunks = (
+ chunksToHighlight: EuiHighlightChunk[],
+ totalLength: number
+) => {
+ const allChunks: EuiHighlightChunk[] = [];
+ const append = (start: number, end: number, highlight: boolean) => {
+ if (end - start > 0) {
+ allChunks.push({
+ start,
+ end,
+ highlight,
+ });
+ }
+ };
+ if (chunksToHighlight.length === 0) {
+ append(0, totalLength, false);
+ } else {
+ let lastIndex = 0;
+ chunksToHighlight.forEach(chunk => {
+ append(lastIndex, chunk.start, false);
+ append(chunk.start, chunk.end, true);
+ lastIndex = chunk.end;
+ });
+ append(lastIndex, totalLength, false);
+ }
+ return allChunks;
+};
+
export const EuiHighlight: FunctionComponent = ({
children,
className,
search,
- strict,
+ strict = false,
+ highlightAll = false,
...rest
}) => {
return (
- {highlight(children, search, strict)}
+ {highlight(children, search, strict, highlightAll)}
);
};