Skip to content

Commit

Permalink
feat: DocsSearch UI
Browse files Browse the repository at this point in the history
  • Loading branch information
bdrtsky committed Jun 26, 2023
1 parent 2605000 commit c08fbd8
Show file tree
Hide file tree
Showing 5 changed files with 289 additions and 109 deletions.
44 changes: 42 additions & 2 deletions components/app/AppHeader.vue
Original file line number Diff line number Diff line change
@@ -1,12 +1,29 @@
<script setup lang="ts">
import { useMagicKeys } from '@vueuse/core'
const { config } = useDocus()
const { navigation } = useContent()
const { y } = useWindowScroll()
const route = useRoute()
const showDocsSearch = ref(true)
const hasDrawer = computed(() => navigation.value?.length > 1 || navigation.value?.[0]?.children?.length)
const isBasicLayout = computed(() => route.meta.layout === 'basic')
const { meta_K, Escape } = useMagicKeys()
watch(meta_K, (v) => {
if (v) {
showDocsSearch.value = !showDocsSearch.value
}
})
watch(Escape, () => {
if (showDocsSearch.value)
showDocsSearch.value = false
})
</script>

<template>
Expand All @@ -21,11 +38,20 @@ const isBasicLayout = computed(() => route.meta.layout === 'basic')
<div class="section center">
<AppHeaderLogo />
<AppNavigation v-if="config.header.navigation" />
<DocsSearchButton
v-else
class="docs-search-button-desktop"
@click="showDocsSearch = true"
/>
<DocsSearch v-model="showDocsSearch" />
</div>

<div class="section right">
<DocsSearch />
<AppTextDirection />
<!-- <AppTextDirection /> -->
<DocsSearchButton
class="docs-search-button-mobile"
@click="showDocsSearch = true"
/>
<AppColorMode />
<div class="social-icons">
<AppSocialIcons />
Expand Down Expand Up @@ -82,6 +108,20 @@ css({
backdropFilter: 'none',
},
'.docs-search-button-desktop': {
display: 'none',
'@lg': {
display: 'flex'
}
},
'.docs-search-button-mobile': {
display: 'flex',
'@lg': {
display: 'none'
}
},
'.header-layout': {
display: 'grid',
height: '100%',
Expand Down
216 changes: 156 additions & 60 deletions components/docs/DocsSearch.vue
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,18 @@
import { useFuse } from '@vueuse/integrations/useFuse'
const { navigation } = useContent()
defineProps({
rememberResult: {
const props = defineProps({
modelValue: {
type: Boolean,
default: true
}
default: false
},
})
const show = ref(false)
const emit = defineEmits(['update:modelValue'])
const q = ref('')
const searchInputRef = ref(null)
const resultsAreaRef = ref(null)
const selected = ref(-1)
Expand Down Expand Up @@ -115,11 +116,22 @@ function go(index) {
const selectedItem = results?.value?.[index]?.item
const path = selectedItem?._path
if (path) {
show.value = false
// show.value = false
emit('update:modelValue')
useRouter().push(path)
}
}
function closeButtonHandler() {
if (q.value) {
q.value = ''
selected.value = -1
searchInputRef.value?.focus()
} else {
emit('update:modelValue')
}
}
// Scroll to selected item on change
watch(selected, value => {
const nextId = results?.value?.[value]?.item?._id
Expand All @@ -133,7 +145,7 @@ watch(
)
// Reset local data when modal closing
watch(show, (value) => {
watch(() => props.modelValue, (value) => {
if (!value) {
q.value = ''
selected.value = -1
Expand All @@ -142,54 +154,90 @@ watch(show, (value) => {
</script>

<template>
<div>
<DocsSearchButton @click="show = !show" />
<Modal v-model="show">
<div class="search-content">
<Modal
:model-value="modelValue"
@update:model-value="$emit('update:modelValue')"
>
<div class="search-content">
<div
class="search-window"
@click.stop
>
<div class="search-input">
<Icon
name="heroicons-outline:search"
class="search-icon"
/>
<input
ref="searchInputRef"
v-model="q"
type="text"
placeholder="Search docs"
@keydown.up.prevent="up"
@keydown.down.prevent="down"
@keydown.enter="go(selected)"
>
<button
class="close-button"
@click="closeButtonHandler"
>
<Icon
name="heroicons:x-mark"
class="close-icon"
/>
</button>
</div>
<div
class="search-window"
@click.stop
v-if="results.length > 0"
ref="resultsAreaRef"
class="search-results"
>
<div class="search-input">
<input
v-model="q"
type="text"
@keydown.up.prevent="up"
@keydown.down.prevent="down"
@keydown.enter="go(selected)"
>
</div>
<div
ref="resultsAreaRef"
class="search-results"
v-for="(result, i) in results"
:id="result.item._id"
:key="result.item._id"
class="search-result"
:class="{ selected: selected === i }"
@click="go(selected)"
@mouseenter.prevent="selected = i"
>
<div
v-for="(result, i) in results"
:id="result.item._id"
:key="result.item._id"
class="search-result"
:class="{ selected: selected === i }"
@click="go(selected)"
>
<div class="wrapper">
<Icon
v-if="getNavItemMeta(result?.item?._path)?.directoryIcon"
:name="getNavItemMeta(result?.item?._path)?.directoryIcon"
/>
<span>
{{ getNavItemMeta(result?.item?._path)?.directoryTitle }}
<span class="arrow">→</span>
</span>
<span>
{{ result.item.title }}
<span class="arrow">→</span>
</span>
<span v-html="highlight(q, result?.matches?.[0] as any)" />
<span
class="search-result-preview"
v-html="highlight(q, result?.matches?.[0] as any)"
/>
</div>
</div>
</div>

<div
v-else-if="!q"
class="search-results empty"
>
Type your query to search docs
</div>

<div
v-else
class="search-results empty"
>
No results found. Try another query
</div>
</div>
</Modal>
</div>
</div>
</Modal>
</template>

<style scoped lang="ts">
Expand All @@ -199,52 +247,100 @@ css({
height: '100%',
display: 'flex',
justifyContent: 'center',
// alignItems: 'center',
'.search-window': {
display: 'flex',
flexDirection: 'column',
// padding: '1rem',
border: '1px solid {elements.border.primary.static}',
backgroundColor: '{elements.surface.primary.backgroundColor}',
borderRadius: '0.5rem',
marginTop: '20vh',
width: '640px',
borderRadius: '{docus.docs.search.results.window.borderRadius}',
marginTop: '{docus.docs.search.results.window.marginTop}',
width: '100%',
maxWidth: '{docus.docs.search.results.window.maxWidth}',
height: 'fit-content',
maxHeight: '320px',
transition: 'all 200ms',
maxHeight: '{docus.docs.search.results.window.maxHeight}',
mx: '{docus.docs.search.results.window.marginX}',
overflow: 'hidden',
backdropFilter: '{docus.docs.search.backdropFilter}',
'.search-input': {
// border: '1px solid {elements.border.primary.static}',
// borderRadius: '0.5rem',
// backgroundColor: '{elements.surface.secondary.backgroundColor}',
backgroundColor: '{color.gray.700}',
// color: '{elements.text.primary.static}',
display: 'flex',
alignItems: 'center',
backgroundColor: '{docus.docs.search.input.backgroundColor}',
'.search-icon': {
color: '{elements.text.tertiary.color.static}',
flexShrink: 0,
marginLeft: '{space.4}',
marginRight: '{space.4}',
width: '{size.20}',
height: '{size.20}',
},
'.close-button': {
display: 'flex',
padding: '{space.3}',
},
'.close-icon': {
color: '{elements.text.secondary.color.static}',
flexShrink: 0,
// padding: '{space.4}',
width: '{size.20}',
height: '{size.20}',
},
input: {
width: '100%',
padding: '0.5rem',
padding: '{space.3} 0',
color: '{elements.text.primary.color.static}',
backgroundColor: 'transparent',
'&:focus, &:focus-visible': {
outline: 'none',
// borderColor: '{elements.border.primary.focus}',
},
'&::placeholder': {
color: '{elements.text.tertiary.color.static}',
}
},
'&:focus, &:focus-visible': {
outline: 'none',
// borderColor: '{elements.border.primary.focus}',
}
},
'.search-results': {
overflow: 'auto',
display: 'flex',
flexDirection: 'column',
'&.empty': {
height: '80px',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
color: '{elements.text.tertiary.color.static}',
}
},
'.search-result': {
my: '0.5rem',
padding: '{space.1} {space.2}',
truncate: true,
display: 'flex',
gap: '0.5rem',
padding: '{space.2}',
cursor: 'pointer',
'&.selected': {
backgroundColor: 'red'
'&.selected .wrapper': {
backgroundColor: '{docus.docs.search.results.selected.backgroundColor}',
},
'.wrapper': {
display: 'flex',
alignItems: 'center',
gap: '{space.1}',
borderRadius: '{radii.2xs}',
padding: '{space.1} {space.2}',
},
svg: {
flexShrink: '0',
opacity: '0.5',
marginRight: '{space.3}',
},
span: {
whiteSpace: 'nowrap',
},
'.arrow': {
opacity: '0.5'
},
'.search-result-preview': {
truncate: true,
},
':deep(mark)': {
color: 'white',
backgroundColor: '{docus.docs.search.results.highlight.backgroundColor}',
}
}
}
Expand Down
Loading

0 comments on commit c08fbd8

Please sign in to comment.