@vuepress-plume/plugin-search 1.0.0-rc.203 → 1.0.0-rc.205

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
- immediate: true,
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) as (SearchResult & Result)[]
132
- // .slice(0, 16)
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
- onKeyStroke('ArrowUp', (event) => {
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
- onKeyStroke('ArrowDown', (event) => {
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: 769px) {
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;
@@ -1,6 +1,5 @@
1
1
  import { ComputedRef, MaybeRef, ShallowRef } from "vue";
2
2
  import { SearchBoxLocales, SearchLocaleOptions } from "../../shared/index.js";
3
-
4
3
  //#region src/client/composables/locale.d.ts
5
4
  /**
6
5
  * Get locale configuration for the current route.
@@ -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 };
@@ -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)) if (anchor) logger.error(`${colors.green("[@vuepress-plume/plugin-search]")} duplicate heading anchor : ${colors.cyan(titles.join(" >> "))} \n at ${colors.cyan(fileId)}`);
162
- else logger.error(`${colors.green("[@vuepress-plume/plugin-search]")} duplicate page permalink : ${colors.cyan(fileId)}`);
163
- else {
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,
@@ -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 / 选择操作的文本 */selectText: string; /** ARIA label for select key / 选择键的 ARIA 标签 */
24
- selectKeyAriaLabel: string; /** Text for navigate action / 导航操作的文本 */
25
- navigateText: string; /** ARIA label for navigate up key / 向上导航键的 ARIA 标签 */
26
- navigateUpKeyAriaLabel: string; /** ARIA label for navigate down key / 向下导航键的 ARIA 标签 */
27
- navigateDownKeyAriaLabel: string; /** Text for close action / 关闭操作的文本 */
28
- closeText: string; /** ARIA label for close key / 关闭键的 ARIA 标签 */
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.203",
4
+ "version": "1.0.0-rc.205",
5
5
  "description": "The Plugin for VuePress 2 - local search",
6
6
  "author": "pengzhanbo <volodymyr@foxmail.com>",
7
7
  "license": "MIT",
@@ -16,33 +16,33 @@
16
16
  },
17
17
  "exports": {
18
18
  ".": {
19
- "types": "./lib/node/index.d.ts",
20
- "import": "./lib/node/index.js"
19
+ "types": "./dist/node/index.d.ts",
20
+ "import": "./dist/node/index.js"
21
21
  },
22
22
  "./client": {
23
- "types": "./lib/client/index.d.ts",
24
- "import": "./lib/client/index.js"
23
+ "types": "./dist/client/index.d.ts",
24
+ "import": "./dist/client/index.js"
25
25
  },
26
26
  "./package.json": "./package.json"
27
27
  },
28
- "main": "lib/node/index.js",
29
- "types": "./lib/node/index.d.ts",
28
+ "main": "dist/node/index.js",
29
+ "types": "dist/node/index.d.ts",
30
30
  "files": [
31
- "lib"
31
+ "dist"
32
32
  ],
33
33
  "peerDependencies": {
34
34
  "vuepress": "2.0.0-rc.30"
35
35
  },
36
36
  "dependencies": {
37
- "@vuepress/helper": "2.0.0-rc.130",
38
- "@vueuse/core": "^14.3.0",
39
- "@vueuse/integrations": "^14.3.0",
37
+ "@vuepress/helper": "2.0.0-rc.131",
38
+ "@vueuse/core": "^14.4.0",
39
+ "@vueuse/integrations": "^14.4.0",
40
40
  "chokidar": "5.0.0",
41
- "focus-trap": "^8.2.1",
41
+ "focus-trap": "^8.2.2",
42
42
  "mark.js": "^8.11.1",
43
43
  "minisearch": "^7.2.0",
44
- "p-map": "^7.0.4",
45
- "vue": "^3.5.35"
44
+ "p-map": "^7.0.6",
45
+ "vue": "^3.5.41"
46
46
  },
47
47
  "publishConfig": {
48
48
  "access": "public",
@@ -56,8 +56,8 @@
56
56
  ],
57
57
  "scripts": {
58
58
  "build": "pnpm run tsdown && pnpm run copy",
59
- "clean": "rimraf --glob ./lib",
60
- "copy": "cpx \"src/**/*.{d.ts,vue,css,scss,jpg,png}\" lib",
59
+ "clean": "rimraf --glob ./dist",
60
+ "copy": "cpx \"src/**/*.{d.ts,vue,css,scss,jpg,png}\" dist",
61
61
  "tsdown": "tsdown --config-loader unrun"
62
62
  }
63
63
  }
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes