|
|
@ -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%;
|
|
|
|