feat(theme): add semantic markup to local search dialog (#2325)

pull/1844/head
Joaquín Sánchez 2 years ago committed by GitHub
parent c9a98ac6bb
commit 4ddb96fe50
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -241,10 +241,12 @@ async function fetchExcerpt(id: string) {
/* Search input focus */ /* Search input focus */
const searchInput = ref<HTMLInputElement>() const searchInput = ref<HTMLInputElement>()
const disableReset = computed(() => {
function focusSearchInput() { return filterText.value?.length <= 0
})
function focusSearchInput(select = true) {
searchInput.value?.focus() searchInput.value?.focus()
searchInput.value?.select() select && searchInput.value?.select()
} }
onMounted(() => { onMounted(() => {
@ -259,11 +261,11 @@ function onSearchBarClick(event: PointerEvent) {
/* Search keyboard selection */ /* Search keyboard selection */
const selectedIndex = ref(0) const selectedIndex = ref(-1)
const disableMouseOver = ref(false) const disableMouseOver = ref(false)
watch(results, () => { watch(results, (r) => {
selectedIndex.value = 0 selectedIndex.value = r.length ? 0 : -1
scrollToSelectedResult() scrollToSelectedResult()
}) })
@ -360,6 +362,11 @@ onBeforeUnmount(() => {
isLocked.value = false isLocked.value = false
}) })
function resetSearch() {
filterText.value = ''
nextTick().then(() => focusSearchInput(false))
}
function formMarkRegex(terms: Set<string>) { function formMarkRegex(terms: Set<string>) {
return new RegExp( return new RegExp(
[...terms] [...terms]
@ -377,34 +384,44 @@ function formMarkRegex(terms: Set<string>) {
<template> <template>
<Teleport to="body"> <Teleport to="body">
<div ref="el" class="VPLocalSearchBox" aria-modal="true"> <div
ref="el"
role="button"
:aria-owns="results?.length ? 'localsearch-list' : undefined"
aria-expanded="true"
aria-haspopup="listbox"
aria-labelledby="localsearch-label"
class="VPLocalSearchBox"
>
<div class="backdrop" @click="$emit('close')" /> <div class="backdrop" @click="$emit('close')" />
<div class="shell"> <div class="shell">
<div class="search-bar" @pointerup="onSearchBarClick($event)"> <form class="search-bar" @pointerup="onSearchBarClick($event)" @submit.prevent="">
<svg <label :title="placeholder" id="localsearch-label" for="localsearch-input">
class="search-icon" <svg
width="18" class="search-icon"
height="18" width="18"
viewBox="0 0 24 24" height="18"
aria-hidden="true" viewBox="0 0 24 24"
> aria-hidden="true"
<g
fill="none"
stroke="currentColor"
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
> >
<circle cx="11" cy="11" r="8" /> <g
<path d="m21 21l-4.35-4.35" /> fill="none"
</g> stroke="currentColor"
</svg> stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
>
<circle cx="11" cy="11" r="8" />
<path d="m21 21l-4.35-4.35" />
</g>
</svg>
</label>
<div class="search-actions before"> <div class="search-actions before">
<button <button
class="back-button" class="back-button"
:title="$t('modal.backButtonTitle')" :title="$t('modal.backButtonTitle')"
@click="$emit('close')" @click="selectedIndex > -1 && $emit('close')"
> >
<svg <svg
width="18" width="18"
@ -427,6 +444,8 @@ function formMarkRegex(terms: Set<string>) {
ref="searchInput" ref="searchInput"
v-model="filterText" v-model="filterText"
:placeholder="placeholder" :placeholder="placeholder"
id="localsearch-input"
aria-labelledby="localsearch-label"
class="search-input" class="search-input"
/> />
<div class="search-actions"> <div class="search-actions">
@ -435,7 +454,7 @@ function formMarkRegex(terms: Set<string>) {
class="toggle-layout-button" class="toggle-layout-button"
:class="{ 'detailed-list': showDetailedList }" :class="{ 'detailed-list': showDetailedList }"
:title="$t('modal.displayDetails')" :title="$t('modal.displayDetails')"
@click="showDetailedList = !showDetailedList" @click="selectedIndex > -1 && (showDetailedList = !showDetailedList)"
> >
<svg <svg
width="18" width="18"
@ -456,8 +475,10 @@ function formMarkRegex(terms: Set<string>) {
<button <button
class="clear-button" class="clear-button"
type="reset"
:disabled="disableReset"
:title="$t('modal.resetButtonTitle')" :title="$t('modal.resetButtonTitle')"
@click="filterText = ''" @click="resetSearch"
> >
<svg <svg
width="18" width="18"
@ -476,65 +497,72 @@ function formMarkRegex(terms: Set<string>) {
</svg> </svg>
</button> </button>
</div> </div>
</div> </form>
<div <ul
ref="resultsEl" ref="resultsEl"
:id="results?.length ? 'localsearch-list' : undefined"
:role="results?.length ? 'listbox' : undefined"
:aria-labelledby="results?.length ? 'localsearch-label' : undefined"
class="results" class="results"
@mousemove="disableMouseOver = false" @mousemove="disableMouseOver = false"
> >
<a <li
v-for="(p, index) in results" v-for="(p, index) in results"
:key="p.id" :key="p.id"
:href="p.id" role="option"
class="result" :aria-selected="selectedIndex === index ? 'true' : 'false'"
:class="{
selected: selectedIndex === index
}"
:aria-label="[...p.titles, p.title].join(' > ')"
@mouseenter="!disableMouseOver && (selectedIndex = index)"
@focusin="selectedIndex = index"
@click="$emit('close')"
> >
<div> <a
<div class="titles"> :href="p.id"
<span class="title-icon">#</span> class="result"
<span v-for="(t, index) in p.titles" :key="index" class="title"> :class="{
<span class="text" v-html="t" /> selected: selectedIndex === index
<svg width="18" height="18" viewBox="0 0 24 24"> }"
<path :aria-label="[...p.titles, p.title].join(' > ')"
fill="none" @mouseenter="!disableMouseOver && (selectedIndex = index)"
stroke="currentColor" @focusin="selectedIndex = index"
stroke-linecap="round" @click="$emit('close')"
stroke-linejoin="round" >
stroke-width="2" <div>
d="m9 18l6-6l-6-6" <div class="titles">
/> <span class="title-icon">#</span>
</svg> <span v-for="(t, index) in p.titles" :key="index" class="title">
</span> <span class="text" v-html="t" />
<span class="title main"> <svg width="18" height="18" viewBox="0 0 24 24">
<span class="text" v-html="p.title" /> <path
</span> fill="none"
</div> stroke="currentColor"
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="m9 18l6-6l-6-6"
/>
</svg>
</span>
<span class="title main">
<span class="text" v-html="p.title" />
</span>
</div>
<div v-if="showDetailedList" class="excerpt-wrapper"> <div v-if="showDetailedList" class="excerpt-wrapper">
<div v-if="p.text" class="excerpt" inert> <div v-if="p.text" class="excerpt" inert>
<div class="vp-doc" v-html="p.text" /> <div class="vp-doc" v-html="p.text" />
</div>
<div class="excerpt-gradient-bottom" />
<div class="excerpt-gradient-top" />
</div> </div>
<div class="excerpt-gradient-bottom" />
<div class="excerpt-gradient-top" />
</div> </div>
</div> </a>
</a> </li>
<li
<div
v-if="filterText && !results.length && enableNoResults" v-if="filterText && !results.length && enableNoResults"
class="no-results" class="no-results"
> >
{{ $t('modal.noResultsText') }} "<strong>{{ filterText }}</strong {{ $t('modal.noResultsText') }} "<strong>{{ filterText }}</strong
>" >"
</div> </li>
</div> </ul>
<div class="search-keyboard-shortcuts"> <div class="search-keyboard-shortcuts">
<span> <span>
@ -692,11 +720,15 @@ function formMarkRegex(terms: Set<string>) {
padding: 8px; padding: 8px;
} }
.search-actions button:hover, .search-actions button:not([disabled]):hover,
.toggle-layout-button.detailed-list { .toggle-layout-button.detailed-list {
color: var(--vp-c-brand); color: var(--vp-c-brand);
} }
.search-actions button.clear-button:disabled {
opacity: 0.37;
}
.search-keyboard-shortcuts { .search-keyboard-shortcuts {
font-size: 0.8rem; font-size: 0.8rem;
opacity: 75%; opacity: 75%;

Loading…
Cancel
Save