#32 enable package search functionality using fuse.js

This commit is contained in:
neil 2022-11-28 16:37:03 +08:00
parent 095040e74e
commit 0660a4efc8
5 changed files with 62 additions and 38 deletions

View file

@ -41,7 +41,8 @@
"type": "module", "type": "module",
"dependencies": { "dependencies": {
"@tauri-apps/api": "^1.2.0", "@tauri-apps/api": "^1.2.0",
"buffer": "^6.0.3" "buffer": "^6.0.3",
"fuse.js": "^6.6.2"
}, },
"pnpm": { "pnpm": {
"onlyBuiltDependencies": [ "onlyBuiltDependencies": [

View file

@ -54,6 +54,10 @@
routes[0].active = false; routes[0].active = false;
} }
}); });
const onSearch = (term: string) => {
console.log('navbar search:', term);
};
</script> </script>
<ul id="NavBar"> <ul id="NavBar">
@ -74,7 +78,7 @@
</a> </a>
</nav> </nav>
<SearchInput size="small" /> <SearchInput size="small" {onSearch} />
{#each routes as route} {#each routes as route}
<li class={route.active ? 'nav_button active' : 'nav_button'}> <li class={route.active ? 'nav_button active' : 'nav_button'}>

View file

@ -1,6 +1,6 @@
<script type="ts"> <script type="ts">
import '$appcss'; import '$appcss';
import Fuse from 'fuse.js';
import { packages as packagesStore, initializePackages } from '$libs/stores'; import { packages as packagesStore, initializePackages } from '$libs/stores';
import type { Package } from '@tea/ui/types'; import type { Package } from '@tea/ui/types';
@ -8,10 +8,18 @@
import SearchInput from '@tea/ui/SearchInput/SearchInput.svelte'; import SearchInput from '@tea/ui/SearchInput/SearchInput.svelte';
import { onMount } from 'svelte'; import { onMount } from 'svelte';
let allPackages: Package[] = [];
let packagesIndex: Fuse<Package>;
let packages: Package[] = []; let packages: Package[] = [];
let initialized = false; let initialized = false;
const searchLimit = 5;
packagesStore.subscribe((v) => { packagesStore.subscribe((v) => {
packages = v; allPackages = v;
packages = allPackages;
packagesIndex = new Fuse(allPackages, {
keys: ['name', 'full_name', 'desc']
});
}); });
onMount(async () => { onMount(async () => {
@ -20,12 +28,34 @@
initializePackages(); initializePackages();
} }
}); });
const onSearch = (term: string) => {
if (term !== '' && term.length > 3) {
const res = packagesIndex.search(term);
packages = [];
for (let i = 0; i < searchLimit; i++) {
if (res[i]) {
packages.push(res[i].item);
}
}
} else {
packages = allPackages;
}
};
function getMatchScore(term: string, pkg: Package) {
// provide higher value with name
const { full_name, desc } = pkg;
const nameScore = stringSimilarity.compareTwoStrings(full_name, term);
const descriptionScore = stringSimilarity.compareTwoStrings(desc, term);
return nameScore * 80 + descriptionScore * 20;
}
</script> </script>
<div class="bg-black border border-gray"> <div class="bg-black border border-gray">
<section class="flex justify-between items-center"> <section class="flex justify-between items-center">
<div> <div>
<SearchInput size="medium" /> <SearchInput size="medium" {onSearch} />
</div> </div>
<div class="pr-4"> <div class="pr-4">
<section class="h-12 w-48 border border-gray" /> <section class="h-12 w-48 border border-gray" />

View file

@ -2,13 +2,23 @@
import './SearchInput.css'; import './SearchInput.css';
export let size: 'small' | 'medium' | 'large' = 'small'; export let size: 'small' | 'medium' | 'large' = 'small';
export let onSearch: (text: string) => void;
let timer: NodeJS.Timeout;
const onChange = (e: KeyboardEvent) => {
const t = e.target as HTMLInputElement;
clearTimeout(timer);
timer = setTimeout(() => {
onSearch && onSearch(t.value);
}, 300);
};
</script> </script>
<section class={`flex items-center ${size}`}> <section class={`flex items-center ${size}`}>
<div class="icon"> <div class="icon">
<i class="icon-search-icon" /> <i class="icon-search-icon" />
</div> </div>
<input type="search" placeholder="search_" /> <input type="search" placeholder="search_" on:keyup={onChange} />
</section> </section>
<!-- <input type="search" class="w-full bg-black h-12 p-4 border border-x-0 border-gray"/> --> <!-- <input type="search" class="w-full bg-black h-12 p-4 border border-x-0 border-gray"/> -->

View file

@ -21,6 +21,7 @@ importers:
eslint: ^8.16.0 eslint: ^8.16.0
eslint-config-prettier: ^8.3.0 eslint-config-prettier: ^8.3.0
eslint-plugin-svelte3: ^4.0.0 eslint-plugin-svelte3: ^4.0.0
fuse.js: ^6.6.2
postcss: ^8.4.19 postcss: ^8.4.19
prettier: ^2.6.2 prettier: ^2.6.2
prettier-plugin-svelte: ^2.7.0 prettier-plugin-svelte: ^2.7.0
@ -35,11 +36,12 @@ importers:
dependencies: dependencies:
'@tauri-apps/api': 1.2.0 '@tauri-apps/api': 1.2.0
buffer: 6.0.3 buffer: 6.0.3
fuse.js: 6.6.2
devDependencies: devDependencies:
'@playwright/test': 1.25.0 '@playwright/test': 1.25.0
'@sveltejs/adapter-auto': 1.0.0-next.89 '@sveltejs/adapter-auto': 1.0.0-next.89
'@sveltejs/adapter-static': 1.0.0-next.48 '@sveltejs/adapter-static': 1.0.0-next.48
'@sveltejs/kit': 1.0.0-next.562_svelte@3.53.1+vite@3.2.4 '@sveltejs/kit': 1.0.0-next.563_svelte@3.53.1+vite@3.2.4
'@tauri-apps/cli': 1.2.0 '@tauri-apps/cli': 1.2.0
'@tea/ui': link:../ui '@tea/ui': link:../ui
'@typescript-eslint/eslint-plugin': 5.43.0_wze2rj5tow7zwqpgbdx2buoy3m '@typescript-eslint/eslint-plugin': 5.43.0_wze2rj5tow7zwqpgbdx2buoy3m
@ -99,7 +101,7 @@ importers:
'@storybook/svelte-vite': 7.0.0-alpha.51_xlscsvjyync2sh57nhuoanpbpq '@storybook/svelte-vite': 7.0.0-alpha.51_xlscsvjyync2sh57nhuoanpbpq
'@storybook/testing-library': 0.0.13_wcqkhtmu7mswc6yz4uyexck3ty '@storybook/testing-library': 0.0.13_wcqkhtmu7mswc6yz4uyexck3ty
'@sveltejs/adapter-auto': 1.0.0-next.89 '@sveltejs/adapter-auto': 1.0.0-next.89
'@sveltejs/kit': 1.0.0-next.561_svelte@3.53.1+vite@3.2.4 '@sveltejs/kit': 1.0.0-next.563_svelte@3.53.1+vite@3.2.4
'@sveltejs/package': 1.0.0-next.1_7dvewpees4iyn2tkw2qzal77a4 '@sveltejs/package': 1.0.0-next.1_7dvewpees4iyn2tkw2qzal77a4
'@typescript-eslint/eslint-plugin': 5.43.0_wze2rj5tow7zwqpgbdx2buoy3m '@typescript-eslint/eslint-plugin': 5.43.0_wze2rj5tow7zwqpgbdx2buoy3m
'@typescript-eslint/parser': 5.43.0_e3uo4sehh4zr4i6m57mkkxxv7y '@typescript-eslint/parser': 5.43.0_e3uo4sehh4zr4i6m57mkkxxv7y
@ -3002,36 +3004,8 @@ packages:
resolution: {integrity: sha512-Z5Z+QZOav6D0KDeU3ReksGERJg/sX1k5OKWWXyQ11OwGErEEwSXHYRUyjaBmZEPeGzpVVGwwMUK8YWJlG/MKeA==} resolution: {integrity: sha512-Z5Z+QZOav6D0KDeU3ReksGERJg/sX1k5OKWWXyQ11OwGErEEwSXHYRUyjaBmZEPeGzpVVGwwMUK8YWJlG/MKeA==}
dev: true dev: true
/@sveltejs/kit/1.0.0-next.561_svelte@3.53.1+vite@3.2.4: /@sveltejs/kit/1.0.0-next.563_svelte@3.53.1+vite@3.2.4:
resolution: {integrity: sha512-N8HQvS6gcm7R78ADfM4xjhuFS3Ir+Ezce3De8WOnISXQ1tS2npc5LMH9LRHHi14nfosAfJ7vUlcLwLE6N/I7+Q==} resolution: {integrity: sha512-RvQSE6dOuH4vE2hM5K/DezJlm9RjC5EMQK8X46mBXIggp8unaDH+YJyF+KRvAZE5sV93Hk5xaq0WZa8jtU42Jw==}
engines: {node: '>=16.14'}
hasBin: true
requiresBuild: true
peerDependencies:
svelte: ^3.44.0
vite: ^3.2.0
dependencies:
'@sveltejs/vite-plugin-svelte': 1.3.1_svelte@3.53.1+vite@3.2.4
'@types/cookie': 0.5.1
cookie: 0.5.0
devalue: 4.2.0
kleur: 4.1.5
magic-string: 0.26.7
mime: 3.0.0
sade: 1.8.1
set-cookie-parser: 2.5.1
sirv: 2.0.2
svelte: 3.53.1
tiny-glob: 0.2.9
undici: 5.12.0
vite: 3.2.4
transitivePeerDependencies:
- diff-match-patch
- supports-color
dev: true
/@sveltejs/kit/1.0.0-next.562_svelte@3.53.1+vite@3.2.4:
resolution: {integrity: sha512-VgJzjtfjVLW/4A/vDtURc10PrS3bb/N62LHzqLZcUNb5+eN4a0k5cayC7Hz2tmtvrb2Qsg+piEAogvqjKBxrOg==}
engines: {node: '>=16.14'} engines: {node: '>=16.14'}
hasBin: true hasBin: true
requiresBuild: true requiresBuild: true
@ -6043,6 +6017,11 @@ packages:
resolution: {integrity: sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==} resolution: {integrity: sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==}
dev: true dev: true
/fuse.js/6.6.2:
resolution: {integrity: sha512-cJaJkxCCxC8qIIcPBF9yGxY0W/tVZS3uEISDxhYIdtk8OL93pe+6Zj7LjCqVV4dzbqcriOZ+kQ/NE4RXZHsIGA==}
engines: {node: '>=10'}
dev: false
/gauge/3.0.2: /gauge/3.0.2:
resolution: {integrity: sha512-+5J6MS/5XksCuXq++uFRsnUd7Ovu1XenbeuIuNRJxYWjgQbPuFhT14lAvsWfqfAmnwluf1OwMjz39HjfLPci0Q==} resolution: {integrity: sha512-+5J6MS/5XksCuXq++uFRsnUd7Ovu1XenbeuIuNRJxYWjgQbPuFhT14lAvsWfqfAmnwluf1OwMjz39HjfLPci0Q==}
engines: {node: '>=10'} engines: {node: '>=10'}