Skip to content

Commit

Permalink
feat(docs): rewrite SidebarSearch in TS (#389)
Browse files Browse the repository at this point in the history
Rewrites `src/components/SidebarSearch.vue` in TypeScript:
- Introduces new interfaces:
  - `SearchResult`: type of an entry in search results
  - `IndexedSearchResult`: type of an entry in sorted search results
  - `SearchResultsSection`: group of search results
- The `sortedResults` computed generates new objects with `map` rather
  than replacing results in place with `forEach` so that no type error
  occurs

A tip for TypeScript migration:
- Explicitly import and register Buefy components so that they are
  type-checked. Note that no type-checking is performed for globally
  registered components.
  • Loading branch information
kikuomax authored Jan 15, 2025
1 parent 0da4f42 commit 904c503
Showing 1 changed file with 56 additions and 25 deletions.
81 changes: 56 additions & 25 deletions packages/docs/src/components/SidebarSearch.vue
Original file line number Diff line number Diff line change
Expand Up @@ -137,15 +137,45 @@
</div>
</template>

<script>
<script lang="ts">
import { defineComponent } from 'vue'
import routes from '@/data/routes'
import menu from '@/data/menu'
import type { PageTree } from '@/data/menu'
import { BIcon, BInput, BModal, BTag } from '@ntohq/buefy-next'
type BInputInstance = InstanceType<typeof BInput>
interface SearchResult {
title: string
subtitle: string
path: string
score?: number
}
interface IndexedSearchResult extends SearchResult {
index: number
}
export default {
interface SearchResultsSection<R extends SearchResult = SearchResult> {
category: string
results: R[]
score: number
}
export default defineComponent({
components: {
BIcon,
BInput,
BModal,
BTag
},
data() {
const categories = new Set()
const categoryByPage = {}
const traverseMenu = (page) => {
const categories = new Set<string>()
const categoryByPage: Record<string, string> = {}
const traverseMenu = (page: PageTree) => {
categories.add(page.category)
page.pages.forEach((subpage) => {
if (typeof subpage === 'string') {
Expand All @@ -162,7 +192,7 @@ export default {
isMacOS: /\(\s*Macintosh\s*;/.test(navigator.userAgent),
categories: [...categories],
categoryByPage,
results: [],
results: [] as SearchResult[],
selectedIndex: 0,
term: ''
}
Expand All @@ -175,8 +205,8 @@ export default {
isTermEmpty() {
return /^\s*$/.test(this.term)
},
sortedResults() {
const resultsByCategory = {}
sortedResults(): SearchResultsSection<IndexedSearchResult>[] {
const resultsByCategory: Record<string, SearchResultsSection> = {}
let index = 0
this.results.forEach((result) => {
Expand Down Expand Up @@ -209,14 +239,15 @@ export default {
})
const sorted = Object.values(resultsByCategory)
.sort((a, b) => String(b.score).localeCompare(a.score))
sorted.forEach((category) => {
category.results = category.results
.sort((a, b) => String(b.score).localeCompare(a.score))
.map((result) => ({ ...result, index: index++ }))
.sort((a, b) => String(b.score).localeCompare(String(a.score)))
return sorted.map((category) => {
return {
...category,
results: category.results
.sort((a, b) => String(b.score).localeCompare(String(a.score)))
.map((result) => ({ ...result, index: index++ }))
}
})
return sorted
}
},
methods: {
Expand All @@ -229,22 +260,22 @@ export default {
this.results = []
},
focus() {
this.$refs.searchbar.focus()
(this.$refs.searchbar as BInputInstance).focus()
},
select(index) {
select(index: number) {
this.selectedIndex = Math.max(0, Math.min(index, this.results.length - 1))
},
search(term) {
search(term: string | number | undefined) {
this.selectedIndex = 0
this.term = term
this.term = term as string
this.results = this.docRoutes.filter((route) => {
const regexp = new RegExp(term.replace(/\s+/g, '.*\\s+'), 'i')
const regexp = new RegExp(this.term.replace(/\s+/g, '.*\\s+'), 'i')
return regexp.test(route.title) || regexp.test(route.subtitle)
})
},
scrollToSelection() {
if (this.$refs[`result_${this.selectedIndex}`]) {
this.$refs[`result_${this.selectedIndex}`][0].scrollIntoView({
(this.$refs[`result_${this.selectedIndex}`] as HTMLElement[])[0].scrollIntoView({
behavior: 'auto',
block: 'center',
inline: 'nearest'
Expand All @@ -263,7 +294,7 @@ export default {
})
)
},
shortcutHandler(event) {
shortcutHandler(event: KeyboardEvent) {
switch (event.key) {
case 'Escape':
return this.close()
Expand Down Expand Up @@ -313,14 +344,14 @@ export default {
break
}
},
highlightTerm(str) {
highlightTerm(str: string) {
const words = this.term.split(/\s+/)
return str.replace(
new RegExp(`(${words.join('|')})`, 'ig'),
'<em class="has-text-primary">$1</em>'
)
},
stripTags(str) {
stripTags(str: string) {
return str.replace(/<[^>]*>/g, '')
}
},
Expand All @@ -330,7 +361,7 @@ export default {
beforeUnmount() {
window.removeEventListener('keydown', this.shortcutHandler)
}
}
})
</script>

<style lang="scss">
Expand Down

0 comments on commit 904c503

Please sign in to comment.