Skip to content

Commit

Permalink
Merge pull request #5 from shhhplus/dev/v2
Browse files Browse the repository at this point in the history
v2.0.0
  • Loading branch information
shhhplus committed May 18, 2023
2 parents bebb43b + 104f497 commit 86e3134
Show file tree
Hide file tree
Showing 8 changed files with 206 additions and 148 deletions.
37 changes: 28 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,25 +1,44 @@
# react-keywords-highlight
# react-highlight-words

[![GitHub license](https://img.shields.io/badge/license-MIT-blue.svg)](https://github.com/shhhplus/react-keywords-highlight/blob/master/LICENSE) [![npm version](https://img.shields.io/npm/v/@shhhplus/react-keywords-highlight.svg?style=flat)](https://www.npmjs.com/package/@shhhplus/react-keywords-highlight) [![codecov](https://img.shields.io/codecov/c/github/shhhplus/react-keywords-highlight/main?token=4MY5JFP8BX)](https://codecov.io/gh/shhhplus/react-keywords-highlight) [![build status](https://img.shields.io/github/actions/workflow/status/shhhplus/react-keywords-highlight/cd.yml)](https://github.com/shhhplus/react-keywords-highlight/actions)
[![GitHub license](https://img.shields.io/badge/license-MIT-blue.svg)](https://github.com/shhhplus/react-highlight-words/blob/master/LICENSE) [![npm version](https://img.shields.io/npm/v/@shhhplus/react-highlight-words.svg?style=flat)](https://www.npmjs.com/package/@shhhplus/react-highlight-words) [![codecov](https://img.shields.io/codecov/c/github/shhhplus/react-highlight-words/main?token=4MY5JFP8BX)](https://codecov.io/gh/shhhplus/react-highlight-words) [![build status](https://img.shields.io/github/actions/workflow/status/shhhplus/react-highlight-words/cd.yml)](https://github.com/shhhplus/react-highlight-words/actions)

A component that can highlight matching keywords with customizable styles.
A component that can highlight matching words with customizable style.

## Install

```sh
npm install @shhhplus/react-keywords-highlight --save
npm install @shhhplus/react-highlight-words --save
```

## How to use
## Usage

```javascript
import KeywordsHighlight from '@shhhplus/react-keywords-highlight';
### Basic

```jsx
import Highlightwords from '@shhhplus/react-highlight-words';

const Demo = () => {
return (
<Highlightwords words="birthday">
Welcome everyone to come and join my birthday party.
</Highlightwords>
);
};
```

### Customize style

```jsx
import Highlightwords from '@shhhplus/react-highlight-words';

const Demo = () => {
return (
<KeywordsHighlight keywords="birthday" style={{ color: 'red' }}>
<Highlightwords
words="birthday"
render={(word) => <span style={{ color: 'red' }}>{word}</span>}
>
Welcome everyone to come and join my birthday party.
</KeywordsHighlight>
</Highlightwords>
);
};
```
20 changes: 10 additions & 10 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

16 changes: 8 additions & 8 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
{
"name": "react-keywords-highlight",
"version": "1.0.3",
"description": "A component that can highlight matching keywords with customizable styles.",
"name": "react-highlight-words",
"version": "2.0.0",
"description": "A component that can highlight matching words with customizable style.",
"keywords": [
"react",
"component",
"keywords",
"highlight"
"highlight",
"keywords"
],
"main": "./src/index.js",
"scripts": {
Expand All @@ -15,14 +15,14 @@
},
"repository": {
"type": "git",
"url": "https://github.com/shhhplus/react-keywords-highlight.git"
"url": "https://github.com/shhhplus/react-highlight-words.git"
},
"author": "shhhplus",
"license": "MIT",
"bugs": {
"url": "https://github.com/shhhplus/react-keywords-highlight/issues"
"url": "https://github.com/shhhplus/react-highlight-words/issues"
},
"homepage": "https://github.com/shhhplus/react-keywords-highlight",
"homepage": "https://github.com/shhhplus/react-highlight-words",
"devDependencies": {
"@babel/core": "^7.21.8",
"@babel/preset-env": "^7.21.5",
Expand Down
2 changes: 1 addition & 1 deletion rollup/rollup.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ export default {
],
},
extra: {
name: '@shhhplus/react-keywords-highlight',
name: '@shhhplus/react-highlight-words',
main: './index.js',
types: './types/index.d.ts',
},
Expand Down
109 changes: 42 additions & 67 deletions src/compile.test.ts
Original file line number Diff line number Diff line change
@@ -1,102 +1,77 @@
import compile from './compile';

test('empty content should works', () => {
expect(compile('', 'party')).toMatchObject([]);
expect(compile('', ['party'])).toMatchObject([]);
});

test('empty keywords should works', () => {
const content = 'Welcome everyone to my party';
expect(compile(content, '')).toMatchObject([content]);
expect(compile(content, [''])).toMatchObject([content]);
expect(compile(content, [])).toMatchObject([content]);
});

test('empty content and empty keywords should works', () => {
expect(compile('', '')).toMatchObject([]);
expect(compile('', [''])).toMatchObject([]);
expect(compile('', [])).toMatchObject([]);
});

test('no matched word should works', () => {
const content = 'Welcome everyone to come and join my birthday party.';
expect(compile(content, 'tom')).toMatchObject([
{
text: content,
matched: false,
},
]);
expect(compile(content, 'tom')).toMatchObject([content]);
});

test('a single matched word should works', () => {
const content = 'Welcome everyone to come and join my birthday party.';
expect(compile(content, 'party')).toMatchObject([
{
text: 'Welcome everyone to come and join my birthday ',
matched: false,
},
{
text: 'party',
matched: true,
},
{
text: '.',
matched: false,
},
'Welcome everyone to come and join my birthday ',
{ text: 'party', matched: true },
'.',
]);
});

test(`multiple matched words should works`, () => {
const content =
'hi, party time. Welcome everyone to come and join my birthday party.';
expect(compile(content, 'party')).toMatchObject([
{
text: 'hi, ',
matched: false,
},
{
text: 'party',
matched: true,
},
{
text: ' time. Welcome everyone to come and join my birthday ',
matched: false,
},
{
text: 'party',
matched: true,
},
{
text: '.',
matched: false,
},
'hi, ',
{ text: 'party', matched: true },
' time. Welcome everyone to come and join my birthday ',
{ text: 'party', matched: true },
'.',
]);
});

test('matched word at begin should works', () => {
const content = 'party.Welcome everyone.';
expect(compile(content, 'party')).toMatchObject([
{
text: 'party',
matched: true,
},
{
text: '.Welcome everyone.',
matched: false,
},
{ text: 'party', matched: true },
'.Welcome everyone.',
]);
});

test('matched word at end should works', () => {
const content = 'Welcome everyone to my party';
expect(compile(content, 'party')).toMatchObject([
{
text: 'Welcome everyone to my ',
matched: false,
},
{
text: 'party',
matched: true,
},
'Welcome everyone to my ',
{ text: 'party', matched: true },
]);
});

test('empty content should works', () => {
expect(compile('', 'party')).toMatchObject([]);
});

test('empty keywords should works', () => {
test('multiple keywords as props should works', () => {
const content = 'Welcome everyone to my party';
expect(compile(content, '')).toMatchObject([
{
text: content,
matched: false,
},

expect(compile(content, ['party', 'to'])).toMatchObject([
'Welcome everyone ',
{ text: 'to', matched: true },
' my ',
{ text: 'party', matched: true },
]);
});

test('empty content and empty keywords should works', () => {
expect(compile('', '')).toMatchObject([]);
expect(compile(content, ['party', 'other'])).toMatchObject([
'Welcome everyone to my ',
{ text: 'party', matched: true },
]);
});
69 changes: 50 additions & 19 deletions src/compile.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,22 @@ type Node = {
matched: boolean;
};

export default function compile(content: string, keywords: string) {
export default function compile(content: string, words: string | string[]) {
let keywords = typeof words === 'string' ? [words] : words;
keywords = keywords.filter((w) => w);
keywords = Array.from(new Set(keywords));

return innerCompile(content, keywords).map((node) => {
return node.matched ? node : node.text;
});
}

const innerCompile = (content: string, keywords: string[]) => {
if (!content) {
return [];
}

if (!keywords) {
if (keywords.length === 0) {
return [
{
text: content,
Expand All @@ -17,21 +27,42 @@ export default function compile(content: string, keywords: string) {
];
}

return content
.split(keywords)
.reduce((acc: Node[], cur, idx) => {
return [
...acc,
{
text: keywords,
matched: true,
},
{
text: cur,
matched: false,
},
];
let nodes: Node[] = [
{
text: content,
matched: false,
},
];
for (let keyword of keywords) {
nodes = nodes.reduce<Node[]>((acc, cur) => {
if (cur.matched) {
return [...acc, cur];
} else {
const list = split(cur.text, keyword);
return [...acc, ...list];
}
}, []);
}
return nodes;
};

const split = (content: string, keyword: string) => {
const nodes = content.split(keyword).map((text) => {
return {
text: text,
matched: false,
};
});
return join(nodes, {
text: keyword,
matched: true,
}).filter((node) => node.text);
};

const join = (arr: Node[], separator: Node) => {
return arr
.reduce((acc: Node[], cur) => {
return [...acc, separator, cur];
}, [])
.slice(1)
.filter((node) => node.text.length);
}
.slice(1);
};
Loading

0 comments on commit 86e3134

Please sign in to comment.