Skip to content

Commit

Permalink
✨ Add highlight-matches component for search functionality
Browse files Browse the repository at this point in the history
  • Loading branch information
jaem1n207 committed Jan 30, 2024
1 parent 643c72f commit 4dd2a80
Show file tree
Hide file tree
Showing 3 changed files with 66 additions and 0 deletions.
Binary file modified bun.lockb
Binary file not shown.
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@
"bits-ui": "^0.15.1",
"clsx": "^2.1.0",
"cmdk-sv": "^0.0.13",
"escape-string-regexp": "^5.0.0",
"lucide-svelte": "^0.312.0",
"mode-watcher": "^0.1.2",
"tailwind-merge": "^2.2.0",
Expand Down
65 changes: 65 additions & 0 deletions src/lib/components/highlight-matches.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
<script lang="ts">
import { browser } from '$app/environment';
import escapeStringRegexp from 'escape-string-regexp';
let className: string;
let highlightedEl: HTMLDivElement | null = null;
export let match = '';
export let value = '';
export { className as class };
/**
* Safely convert a user's search query to a regular expression.
* @see https://github.com/jaem1n207/lazy-dev/issues/65
*/
const processSearchTerm = (searchTerm: string): RegExp => {
const trimmedSearchTerm = searchTerm.trim();
const searchWords = trimmedSearchTerm.split(/\s+/).filter(Boolean);
if (searchWords.length > 0) {
const escapedSearch = searchWords.map(escapeStringRegexp).join('|');
return new RegExp(escapedSearch, 'ig');
}
return /$.^/gi;
};
/**
* Generate search results, and highlight the parts that match your search terms.
*/
const createSearchResult = (value: string, regexp: RegExp) => {
const splitValue = value.split('');
let index = 0;
const content: (string | Node)[] = [];
let result = regexp.exec(value);
while (result) {
if (result.index === regexp.lastIndex) {
regexp.lastIndex++;
} else {
const before = splitValue.splice(0, result.index - index).join('');
const matched = splitValue.splice(0, regexp.lastIndex - result.index).join('');
content.push(before, `<span class="text-tertiary">${matched}</span>`);
index = regexp.lastIndex;
}
result = regexp.exec(value);
}
return { content, remaining: splitValue.join('') };
};
$: {
if (browser && value) {
const regexp = processSearchTerm(match);
const { content, remaining } = createSearchResult(value, regexp);
if (highlightedEl) {
highlightedEl.innerHTML = content.join('') + remaining;
}
}
}
</script>

<div class={className} bind:this={highlightedEl}></div>

0 comments on commit 4dd2a80

Please sign in to comment.