@vuepress-plume/plugin-search 1.0.0-rc.204 → 1.0.0-rc.206
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
|
@@ -55,9 +55,12 @@ interface Result {
|
|
|
55
55
|
text?: string
|
|
56
56
|
}
|
|
57
57
|
|
|
58
|
-
const { activate } = useFocusTrap(el, {
|
|
59
|
-
|
|
60
|
-
|
|
58
|
+
const { activate } = useFocusTrap(el, { immediate: true })
|
|
59
|
+
|
|
60
|
+
const isSearchIndexLoading = ref(false)
|
|
61
|
+
const isSearching = ref(false)
|
|
62
|
+
|
|
63
|
+
const showSearchSpinner = computed(() => isSearchIndexLoading.value || isSearching.value)
|
|
61
64
|
|
|
62
65
|
const searchIndex = computedAsync(async () => {
|
|
63
66
|
let tokenize: ((str: string) => string[]) | undefined
|
|
@@ -83,7 +86,7 @@ const searchIndex = computedAsync(async () => {
|
|
|
83
86
|
},
|
|
84
87
|
),
|
|
85
88
|
)
|
|
86
|
-
})
|
|
89
|
+
}, undefined, isSearchIndexLoading)
|
|
87
90
|
|
|
88
91
|
const disableQueryPersistence = computed(() =>
|
|
89
92
|
options?.disableQueryPersistence === true,
|
|
@@ -121,15 +124,19 @@ watchDebounced(
|
|
|
121
124
|
let canceled = false
|
|
122
125
|
onCleanup(() => {
|
|
123
126
|
canceled = true
|
|
127
|
+
isSearching.value = false
|
|
124
128
|
})
|
|
125
129
|
|
|
126
|
-
if (!index)
|
|
130
|
+
if (!index) {
|
|
131
|
+
results.value = []
|
|
127
132
|
return
|
|
133
|
+
}
|
|
128
134
|
|
|
135
|
+
isSearching.value = true
|
|
129
136
|
// Search
|
|
130
137
|
results.value = index
|
|
131
|
-
.search(filterTextValue)
|
|
132
|
-
|
|
138
|
+
.search(filterTextValue)
|
|
139
|
+
.slice(0, 16) as (SearchResult & Result)[]
|
|
133
140
|
enableNoResults.value = true
|
|
134
141
|
|
|
135
142
|
const terms = new Set<string>()
|
|
@@ -155,6 +162,7 @@ watchDebounced(
|
|
|
155
162
|
},
|
|
156
163
|
})
|
|
157
164
|
})
|
|
165
|
+
isSearching.value = false
|
|
158
166
|
},
|
|
159
167
|
{ debounce: 200, immediate: true },
|
|
160
168
|
)
|
|
@@ -202,7 +210,17 @@ function scrollToSelectedResult() {
|
|
|
202
210
|
})
|
|
203
211
|
}
|
|
204
212
|
|
|
205
|
-
|
|
213
|
+
function isMacCtrlShortcut(event: KeyboardEvent) {
|
|
214
|
+
return (
|
|
215
|
+
event.ctrlKey
|
|
216
|
+
&& !event.altKey
|
|
217
|
+
&& !event.metaKey
|
|
218
|
+
&& !event.shiftKey
|
|
219
|
+
&& document.documentElement.classList.contains('mac')
|
|
220
|
+
)
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
function selectPreviousResult(event: KeyboardEvent) {
|
|
206
224
|
event.preventDefault()
|
|
207
225
|
selectedIndex.value--
|
|
208
226
|
if (selectedIndex.value < 0)
|
|
@@ -210,9 +228,9 @@ onKeyStroke('ArrowUp', (event) => {
|
|
|
210
228
|
|
|
211
229
|
disableMouseOver.value = true
|
|
212
230
|
scrollToSelectedResult()
|
|
213
|
-
}
|
|
231
|
+
}
|
|
214
232
|
|
|
215
|
-
|
|
233
|
+
function selectNextResult(event: KeyboardEvent) {
|
|
216
234
|
event.preventDefault()
|
|
217
235
|
selectedIndex.value++
|
|
218
236
|
if (selectedIndex.value >= results.value.length)
|
|
@@ -220,6 +238,19 @@ onKeyStroke('ArrowDown', (event) => {
|
|
|
220
238
|
|
|
221
239
|
disableMouseOver.value = true
|
|
222
240
|
scrollToSelectedResult()
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
onKeyStroke('ArrowUp', selectPreviousResult)
|
|
244
|
+
onKeyStroke('ArrowDown', selectNextResult)
|
|
245
|
+
onKeyStroke(['p', 'P'], (event) => {
|
|
246
|
+
if (isMacCtrlShortcut(event)) {
|
|
247
|
+
selectPreviousResult(event)
|
|
248
|
+
}
|
|
249
|
+
})
|
|
250
|
+
onKeyStroke(['n', 'N'], (event) => {
|
|
251
|
+
if (isMacCtrlShortcut(event)) {
|
|
252
|
+
selectNextResult(event)
|
|
253
|
+
}
|
|
223
254
|
})
|
|
224
255
|
|
|
225
256
|
const router = useRouter()
|
|
@@ -344,6 +375,13 @@ function selectedClick(e: MouseEvent, p: SearchResult & Result) {
|
|
|
344
375
|
class="search-input"
|
|
345
376
|
>
|
|
346
377
|
<div class="search-actions">
|
|
378
|
+
<span
|
|
379
|
+
class="search-loading"
|
|
380
|
+
:class="{ active: showSearchSpinner }"
|
|
381
|
+
:role="showSearchSpinner ? 'status' : undefined"
|
|
382
|
+
aria-live="polite"
|
|
383
|
+
:aria-label="showSearchSpinner ? 'Loading search results' : undefined"
|
|
384
|
+
/>
|
|
347
385
|
<button
|
|
348
386
|
class="clear-button"
|
|
349
387
|
type="reset"
|
|
@@ -604,12 +642,40 @@ svg {
|
|
|
604
642
|
}
|
|
605
643
|
}
|
|
606
644
|
|
|
607
|
-
@media (min-width:
|
|
645
|
+
@media (min-width: 768px) {
|
|
608
646
|
.search-actions.before {
|
|
609
647
|
display: none;
|
|
610
648
|
}
|
|
611
649
|
}
|
|
612
650
|
|
|
651
|
+
.search-loading {
|
|
652
|
+
flex: none;
|
|
653
|
+
width: 18px;
|
|
654
|
+
height: 18px;
|
|
655
|
+
margin: 8px;
|
|
656
|
+
visibility: hidden;
|
|
657
|
+
border: 2px solid var(--vp-c-divider);
|
|
658
|
+
border-top-color: var(--vp-c-brand-1);
|
|
659
|
+
border-radius: 50%;
|
|
660
|
+
}
|
|
661
|
+
|
|
662
|
+
.search-loading.active {
|
|
663
|
+
visibility: visible;
|
|
664
|
+
animation: local-search-loading 0.8s linear infinite;
|
|
665
|
+
}
|
|
666
|
+
|
|
667
|
+
@media (prefers-reduced-motion: reduce) {
|
|
668
|
+
.search-loading.active {
|
|
669
|
+
animation: none;
|
|
670
|
+
}
|
|
671
|
+
}
|
|
672
|
+
|
|
673
|
+
@keyframes local-search-loading {
|
|
674
|
+
to {
|
|
675
|
+
transform: rotate(360deg);
|
|
676
|
+
}
|
|
677
|
+
}
|
|
678
|
+
|
|
613
679
|
.search-actions button {
|
|
614
680
|
padding: 7px;
|
|
615
681
|
margin: 0;
|
package/dist/node/index.d.ts
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { SearchOptions, SearchPluginOptions } from "../shared/index.js";
|
|
2
2
|
import { App, Plugin } from "vuepress/core";
|
|
3
3
|
export * from "../shared/index.js";
|
|
4
|
-
|
|
5
4
|
//#region src/node/prepareSearchIndex.d.ts
|
|
6
5
|
/**
|
|
7
6
|
* Options for search index preparation.
|
|
@@ -32,11 +31,7 @@ interface SearchIndexOptions {
|
|
|
32
31
|
* @param options.isSearchable - Function to filter searchable pages / 过滤可搜索页面的函数
|
|
33
32
|
* @param options.searchOptions - MiniSearch configuration / MiniSearch 配置
|
|
34
33
|
*/
|
|
35
|
-
declare function prepareSearchIndex({
|
|
36
|
-
app,
|
|
37
|
-
isSearchable,
|
|
38
|
-
searchOptions
|
|
39
|
-
}: SearchIndexOptions): Promise<void>;
|
|
34
|
+
declare function prepareSearchIndex({ app, isSearchable, searchOptions }: SearchIndexOptions): Promise<void>;
|
|
40
35
|
//#endregion
|
|
41
36
|
//#region src/node/searchPlugin.d.ts
|
|
42
37
|
/**
|
|
@@ -69,10 +64,6 @@ declare function prepareSearchIndex({
|
|
|
69
64
|
* ]
|
|
70
65
|
* }
|
|
71
66
|
*/
|
|
72
|
-
declare function searchPlugin({
|
|
73
|
-
locales,
|
|
74
|
-
isSearchable,
|
|
75
|
-
...searchOptions
|
|
76
|
-
}?: SearchPluginOptions): Plugin;
|
|
67
|
+
declare function searchPlugin({ locales, isSearchable, ...searchOptions }?: SearchPluginOptions): Plugin;
|
|
77
68
|
//#endregion
|
|
78
69
|
export { prepareSearchIndex, searchPlugin };
|
package/dist/node/index.js
CHANGED
|
@@ -158,9 +158,10 @@ ${page.contentRendered}`);
|
|
|
158
158
|
if (!section || !(section.text || section.titles)) break;
|
|
159
159
|
const { anchor, text, titles } = section;
|
|
160
160
|
const id = anchor ? [fileId, anchor].join("#") : fileId;
|
|
161
|
-
if (index.has(id))
|
|
162
|
-
|
|
163
|
-
|
|
161
|
+
if (index.has(id)) {
|
|
162
|
+
if (anchor) logger.error(`${colors.green("[@vuepress-plume/plugin-search]")} duplicate heading anchor : ${colors.cyan(titles.join(" >> "))} \n at ${colors.cyan(fileId)}`);
|
|
163
|
+
else logger.error(`${colors.green("[@vuepress-plume/plugin-search]")} duplicate page permalink : ${colors.cyan(fileId)}`);
|
|
164
|
+
} else {
|
|
164
165
|
const item = {
|
|
165
166
|
id,
|
|
166
167
|
text,
|
package/dist/shared/index.d.ts
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { Options } from "minisearch";
|
|
2
2
|
import { LocaleConfig, Page } from "vuepress/core";
|
|
3
|
-
|
|
4
3
|
//#region src/shared/index.d.ts
|
|
5
4
|
/**
|
|
6
5
|
* Locale options for search UI strings.
|
|
@@ -20,12 +19,19 @@ interface SearchLocaleOptions {
|
|
|
20
19
|
noResultsText: string;
|
|
21
20
|
/** Footer keyboard shortcut hints / 底部键盘快捷键提示 */
|
|
22
21
|
footer: {
|
|
23
|
-
/** Text for select action / 选择操作的文本 */
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
22
|
+
/** Text for select action / 选择操作的文本 */
|
|
23
|
+
selectText: string;
|
|
24
|
+
/** ARIA label for select key / 选择键的 ARIA 标签 */
|
|
25
|
+
selectKeyAriaLabel: string;
|
|
26
|
+
/** Text for navigate action / 导航操作的文本 */
|
|
27
|
+
navigateText: string;
|
|
28
|
+
/** ARIA label for navigate up key / 向上导航键的 ARIA 标签 */
|
|
29
|
+
navigateUpKeyAriaLabel: string;
|
|
30
|
+
/** ARIA label for navigate down key / 向下导航键的 ARIA 标签 */
|
|
31
|
+
navigateDownKeyAriaLabel: string;
|
|
32
|
+
/** Text for close action / 关闭操作的文本 */
|
|
33
|
+
closeText: string;
|
|
34
|
+
/** ARIA label for close key / 关闭键的 ARIA 标签 */
|
|
29
35
|
closeKeyAriaLabel: string;
|
|
30
36
|
};
|
|
31
37
|
}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vuepress-plume/plugin-search",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "1.0.0-rc.
|
|
4
|
+
"version": "1.0.0-rc.206",
|
|
5
5
|
"description": "The Plugin for VuePress 2 - local search",
|
|
6
6
|
"author": "pengzhanbo <volodymyr@foxmail.com>",
|
|
7
7
|
"license": "MIT",
|
|
@@ -35,14 +35,14 @@
|
|
|
35
35
|
},
|
|
36
36
|
"dependencies": {
|
|
37
37
|
"@vuepress/helper": "2.0.0-rc.131",
|
|
38
|
-
"@vueuse/core": "^14.
|
|
39
|
-
"@vueuse/integrations": "^14.
|
|
38
|
+
"@vueuse/core": "^14.4.0",
|
|
39
|
+
"@vueuse/integrations": "^14.4.0",
|
|
40
40
|
"chokidar": "5.0.0",
|
|
41
41
|
"focus-trap": "^8.2.2",
|
|
42
42
|
"mark.js": "^8.11.1",
|
|
43
43
|
"minisearch": "^7.2.0",
|
|
44
|
-
"p-map": "^7.0.
|
|
45
|
-
"vue": "^3.5.
|
|
44
|
+
"p-map": "^7.0.7",
|
|
45
|
+
"vue": "^3.5.42"
|
|
46
46
|
},
|
|
47
47
|
"publishConfig": {
|
|
48
48
|
"access": "public",
|