{
window.open(url);
}}
+ onMouseEnter={() => {
+ handleResultIndex(index);
+ }}
>
{type === 'repo' ? (
diff --git a/src/contentScripts/searchBar/app/SearchResult/style.less b/src/contentScripts/searchBar/app/SearchResult/style.less
index 7d43409..3b59d09 100644
--- a/src/contentScripts/searchBar/app/SearchResult/style.less
+++ b/src/contentScripts/searchBar/app/SearchResult/style.less
@@ -28,9 +28,12 @@
transition: background-color 300ms ease-in-out;
}
+.selected {
+ background: @item-hover-bg;
+}
[theme='light'] {
.row:hover {
- background: @item-hover-bg;
+ //.selected;
}
}
diff --git a/src/contentScripts/searchBar/app/index.tsx b/src/contentScripts/searchBar/app/index.tsx
index 49113d3..3cf6d2e 100644
--- a/src/contentScripts/searchBar/app/index.tsx
+++ b/src/contentScripts/searchBar/app/index.tsx
@@ -6,6 +6,7 @@ import { Button, Space } from 'antd';
import useSearchBarService, { SearchBarService } from './useSearchBarService';
import { SearchService, useSearchService } from './useSearchService';
import { useCheckTokenValidService } from './useCheckTokenValidService';
+import { KeyboardService, useKeyboardService } from './useKeyboardService';
import SearchInput from './SearchInput';
import SearchResult from './SearchResult';
@@ -19,58 +20,61 @@ const SearchBar: FC = () => {
const valid = useCheckTokenValidService();
+ const keyboardService = useKeyboardService();
return visible ? (
-
-
- {valid ? (
- <>
-
-
-
-
- >
- ) : (
-
-
-
-
-
-
Token 无效
-
-
- 没有配置 token 或 token 无效,请前往配置页添加 token。
- 添加完成后请刷新页面并重试。
-
-
-
-
+
+
+
+ {valid ? (
+ <>
+
+
+
+
+ >
+ ) : (
+
+
+
+
+
+
Token 无效
+
+
+ 没有配置 token 或 token 无效,请前往配置页添加 token。
+ 添加完成后请刷新页面并重试。
+
+
+
+
+
-
- )}
+ )}
+
-
+
) : null;
};
diff --git a/src/contentScripts/searchBar/app/SearchInput/useKeyboardService.ts b/src/contentScripts/searchBar/app/useKeyboardService.ts
similarity index 54%
rename from src/contentScripts/searchBar/app/SearchInput/useKeyboardService.ts
rename to src/contentScripts/searchBar/app/useKeyboardService.ts
index 3ef29d5..de413ee 100644
--- a/src/contentScripts/searchBar/app/SearchInput/useKeyboardService.ts
+++ b/src/contentScripts/searchBar/app/useKeyboardService.ts
@@ -2,39 +2,52 @@ import { useContext, useEffect, useRef, useState } from 'react';
import type { Input } from 'antd';
import { getServiceToken } from '@/utils';
-import { SearchService } from '../useSearchService';
-import { SearchBarService } from '../useSearchBarService';
+
+import { SearchService } from './useSearchService';
+import { SearchBarService } from './useSearchBarService';
/**
* Keyboard 需要的状态
*/
export const useKeyboardService = () => {
- const { setType, optionKeys, optionActiveIndex, isEmpty } = useContext(
- SearchService,
- );
+ const {
+ setType,
+ optionKeys,
+ optionActiveIndex,
+ isEmpty,
+ result,
+ } = useContext(SearchService);
const { hide } = useContext(SearchBarService);
const [focusKey, setFocusKey] = useState
('input');
const inputRef = useRef(null);
+ const [selectResultIndex, setSelectResultIndex] = useState(0);
+ const handleIndex = (index: number, length: number, back?: boolean) => {
+ if (back) {
+ return index === 0 ? length - 1 : index - 1;
+ }
+ return index === length - 1 ? 0 : index + 1;
+ };
/**
* 按 Tabs 键切换选中 type
* @param back
*/
const switchOptionIndex = (back?: boolean) => {
- let newIndex: number;
-
- if (back) {
- newIndex =
- optionActiveIndex === 0 ? optionKeys.length - 1 : optionActiveIndex - 1;
- } else {
- newIndex =
- optionActiveIndex === optionKeys.length - 1 ? 0 : optionActiveIndex + 1;
- }
+ const newIndex = handleIndex(optionActiveIndex, optionKeys.length, back);
setType(optionKeys[newIndex]);
};
+ /**
+ * 按 上下键切换选中的 result
+ * @param back
+ */
+ const switchResultIndex = (back?: boolean) => {
+ const newIndex = handleIndex(selectResultIndex, result.length, back);
+ setSelectResultIndex(newIndex);
+ };
+
const focusOnInput = () => {
inputRef.current?.focus();
setFocusKey('input');
@@ -48,6 +61,11 @@ export const useKeyboardService = () => {
setFocusKey('result');
};
+ const handleResultIndex = (index: number) => {
+ focusOnResult();
+ setSelectResultIndex(index);
+ };
+
// 将焦点切换到 Options
const onKeyDown = (event: KeyboardEvent) => {
// 焦点在 options 的情况
@@ -65,6 +83,10 @@ export const useKeyboardService = () => {
case 'ArrowLeft':
switchOptionIndex(true);
break;
+ case 'ArrowDown':
+ event.preventDefault();
+ focusOnResult();
+ break;
case 'ArrowUp':
case 'Escape':
focusOnInput();
@@ -95,6 +117,37 @@ export const useKeyboardService = () => {
default:
}
}
+
+ if (focusKey === 'result') {
+ switch (event.key) {
+ case 'Tab':
+ event.preventDefault();
+ switchOptionIndex(event.shiftKey);
+ break;
+ case 'ArrowDown':
+ event.preventDefault();
+ switchResultIndex();
+ break;
+ case 'ArrowUp':
+ event.preventDefault();
+ if (selectResultIndex === 0) {
+ focusOnOptions();
+ } else {
+ switchResultIndex(true);
+ }
+ break;
+ case 'ArrowRight':
+ switchOptionIndex();
+ break;
+ case 'ArrowLeft':
+ switchOptionIndex(true);
+ break;
+ case 'Escape':
+ focusOnInput();
+ break;
+ default:
+ }
+ }
};
useEffect(() => {
@@ -103,12 +156,13 @@ export const useKeyboardService = () => {
return () => {
window.onkeydown = null;
};
- }, [focusKey, optionActiveIndex, isEmpty]);
+ }, [focusKey, optionActiveIndex, isEmpty, selectResultIndex]);
return {
focusKey,
inputRef,
-
+ selectResultIndex,
+ handleResultIndex,
onKeyDown,
focusOnInput,
focusOnOptions,
diff --git a/src/contentScripts/searchBar/app/useSearchService.ts b/src/contentScripts/searchBar/app/useSearchService.ts
index 1d61179..5c397db 100644
--- a/src/contentScripts/searchBar/app/useSearchService.ts
+++ b/src/contentScripts/searchBar/app/useSearchService.ts
@@ -1,5 +1,5 @@
import type { ChangeEvent } from 'react';
-import { useMemo, useState } from 'react';
+import { useEffect, useMemo, useState } from 'react';
import { useDebounce } from 'ahooks';
import { useEventCallback } from 'rxjs-hooks';
@@ -38,23 +38,18 @@ export const useSearchService = () => {
}),
);
- // TEST 用于测试 list 的代码
- // useEffect(() => {
- // fetch({ q: '设计', type: 'repo', related: true }).then();
- // }, []);
-
// 搜索类型
const [type, setType] = useState('repo');
// 与我相关
const [related, setRelated] = useState(true);
const [loading, setLoading] = useState(false);
- const defaultState = { data: [], total: 0 };
+ const defaultState = { result: [], total: 0 };
/**
* 搜索方法
*/
- const [onSearchEvent, { total, data }] = useEventCallback<
+ const [onSearchEvent, { total, result }] = useEventCallback<
ChangeEvent,
SearchBar.SearchData,
[boolean, SearchBar.SearchType]
@@ -87,27 +82,33 @@ export const useSearchService = () => {
if (!response) return defaultState;
// 之后在这一步做值解构
- const { data: result, meta } = response;
- return { data: result, total: meta?.total };
+ const { data, meta } = response;
+ return { result: data, total: meta?.total };
}),
),
defaultState,
[related, type],
);
+ // TEST 用于测试 list 的代码
+ useEffect(() => {
+ // @ts-ignore
+ onSearchEvent({ target: { value: '设计' } });
+ }, []);
+
return {
options,
optionKeys: options.map((o) => o.key),
optionActiveIndex: options.findIndex((o) => o.key === type),
total,
- result: data,
+ result,
loading: useDebounce(loading, { wait: 500 }),
onSearchEvent,
type,
setType,
related,
setRelated,
- isEmpty: data.length === 0,
+ isEmpty: result.length === 0,
};
};
diff --git a/types/SearchBar.d.ts b/types/SearchBar.d.ts
index 2063a61..9c1fa0f 100644
--- a/types/SearchBar.d.ts
+++ b/types/SearchBar.d.ts
@@ -25,7 +25,7 @@ declare module SearchBar {
meta: Meta;
}
export interface SearchData {
- data: yuque.SearchResponseData[];
+ result: yuque.SearchResponseData[];
total: number;
}
/**