refactor(search-bar): improved tool fuzzy search
This commit is contained in:
		
							parent
							
								
									8476cf319b
								
							
						
					
					
						commit
						1b5d4e72bd
					
				| @ -45,6 +45,7 @@ | |||||||
|     "crypto-js": "^4.1.1", |     "crypto-js": "^4.1.1", | ||||||
|     "date-fns": "^2.29.3", |     "date-fns": "^2.29.3", | ||||||
|     "figue": "^1.2.0", |     "figue": "^1.2.0", | ||||||
|  |     "fuse.js": "^6.6.2", | ||||||
|     "highlight.js": "^11.6.0", |     "highlight.js": "^11.6.0", | ||||||
|     "json5": "^2.2.1", |     "json5": "^2.2.1", | ||||||
|     "lodash": "^4.17.21", |     "lodash": "^4.17.21", | ||||||
|  | |||||||
							
								
								
									
										7
									
								
								pnpm-lock.yaml
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										7
									
								
								pnpm-lock.yaml
									
									
									
										generated
									
									
									
								
							| @ -38,6 +38,7 @@ specifiers: | |||||||
|   eslint-plugin-import: ^2.26.0 |   eslint-plugin-import: ^2.26.0 | ||||||
|   eslint-plugin-vue: ^8.7.1 |   eslint-plugin-vue: ^8.7.1 | ||||||
|   figue: ^1.2.0 |   figue: ^1.2.0 | ||||||
|  |   fuse.js: ^6.6.2 | ||||||
|   highlight.js: ^11.6.0 |   highlight.js: ^11.6.0 | ||||||
|   jsdom: ^19.0.0 |   jsdom: ^19.0.0 | ||||||
|   json5: ^2.2.1 |   json5: ^2.2.1 | ||||||
| @ -81,6 +82,7 @@ dependencies: | |||||||
|   crypto-js: 4.1.1 |   crypto-js: 4.1.1 | ||||||
|   date-fns: 2.29.3 |   date-fns: 2.29.3 | ||||||
|   figue: 1.2.0 |   figue: 1.2.0 | ||||||
|  |   fuse.js: 6.6.2 | ||||||
|   highlight.js: 11.6.0 |   highlight.js: 11.6.0 | ||||||
|   json5: 2.2.1 |   json5: 2.2.1 | ||||||
|   lodash: 4.17.21 |   lodash: 4.17.21 | ||||||
| @ -4054,6 +4056,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 | ||||||
|  | 
 | ||||||
|   /gensync/1.0.0-beta.2: |   /gensync/1.0.0-beta.2: | ||||||
|     resolution: {integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==} |     resolution: {integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==} | ||||||
|     engines: {node: '>=6.9.0'} |     engines: {node: '>=6.9.0'} | ||||||
|  | |||||||
| @ -1,30 +1,22 @@ | |||||||
| <script lang="ts" setup> | <script lang="ts" setup> | ||||||
|  | import { useFuzzySearch } from '@/composable/fuzzySearch'; | ||||||
| import { tools } from '@/tools'; | import { tools } from '@/tools'; | ||||||
| import { SearchRound } from '@vicons/material'; | import { SearchRound } from '@vicons/material'; | ||||||
| import { useMagicKeys, whenever } from '@vueuse/core'; | import { useMagicKeys, whenever } from '@vueuse/core'; | ||||||
| import { deburr } from 'lodash'; |  | ||||||
| import { computed, ref } from 'vue'; | import { computed, ref } from 'vue'; | ||||||
| import { useRouter } from 'vue-router'; | import { useRouter } from 'vue-router'; | ||||||
| 
 | 
 | ||||||
| const router = useRouter(); | const router = useRouter(); | ||||||
| const queryString = ref(''); | const queryString = ref(''); | ||||||
| 
 | 
 | ||||||
| const cleanString = (s: string) => deburr(s.trim().toLowerCase()); | const { searchResult } = useFuzzySearch({ | ||||||
| 
 |   search: queryString, | ||||||
| const searchableTools = tools.map(({ name, description, keywords, path }) => ({ |   data: tools, | ||||||
|   searchableText: [name, description, ...keywords].map(cleanString).join(' '), |   options: { keys: [{ name: 'name', weight: 2 }, 'description', 'keywords'] }, | ||||||
|   path, |  | ||||||
|   name, |  | ||||||
| })); |  | ||||||
| 
 |  | ||||||
| const options = computed(() => { |  | ||||||
|   const query = cleanString(queryString.value); |  | ||||||
| 
 |  | ||||||
|   return searchableTools |  | ||||||
|     .filter(({ searchableText }) => searchableText.includes(query)) |  | ||||||
|     .map(({ name, path }) => ({ label: name, value: path })); |  | ||||||
| }); | }); | ||||||
| 
 | 
 | ||||||
|  | const options = computed(() => searchResult.value.map(({ name, path }) => ({ label: name, value: path }))); | ||||||
|  | 
 | ||||||
| function onSelect(path: string) { | function onSelect(path: string) { | ||||||
|   router.push(path); |   router.push(path); | ||||||
|   queryString.value = ''; |   queryString.value = ''; | ||||||
| @ -52,7 +44,7 @@ whenever(keys.ctrl_k, () => { | |||||||
|       v-model:value="queryString" |       v-model:value="queryString" | ||||||
|       :options="options" |       :options="options" | ||||||
|       :input-props="{ autocomplete: 'disabled' }" |       :input-props="{ autocomplete: 'disabled' }" | ||||||
|       :on-select="onSelect" |       :on-select="(value) => onSelect(String(value))" | ||||||
|     > |     > | ||||||
|       <template #default="{ handleInput, handleBlur, handleFocus, value: slotValue }"> |       <template #default="{ handleInput, handleBlur, handleFocus, value: slotValue }"> | ||||||
|         <n-input |         <n-input | ||||||
|  | |||||||
							
								
								
									
										23
									
								
								src/composable/fuzzySearch.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										23
									
								
								src/composable/fuzzySearch.ts
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,23 @@ | |||||||
|  | import { get, type MaybeRef } from '@vueuse/core'; | ||||||
|  | import Fuse from 'fuse.js'; | ||||||
|  | import { computed } from 'vue'; | ||||||
|  | 
 | ||||||
|  | export { useFuzzySearch }; | ||||||
|  | 
 | ||||||
|  | function useFuzzySearch<Data>({ | ||||||
|  |   search, | ||||||
|  |   data, | ||||||
|  |   options = {}, | ||||||
|  | }: { | ||||||
|  |   search: MaybeRef<string>; | ||||||
|  |   data: Data[]; | ||||||
|  |   options?: Fuse.IFuseOptions<Data>; | ||||||
|  | }) { | ||||||
|  |   const fuse = new Fuse(data, options); | ||||||
|  | 
 | ||||||
|  |   const searchResult = computed(() => { | ||||||
|  |     return fuse.search(get(search)).map(({ item }) => item); | ||||||
|  |   }); | ||||||
|  | 
 | ||||||
|  |   return { searchResult }; | ||||||
|  | } | ||||||
		Loading…
	
		Reference in New Issue
	
	Block a user