diff --git a/src/contentScripts/searchBar/app/SearchInput/components/Options/index.tsx b/src/contentScripts/searchBar/app/SearchInput/components/Options/index.tsx index f2f9768..d2eae50 100644 --- a/src/contentScripts/searchBar/app/SearchInput/components/Options/index.tsx +++ b/src/contentScripts/searchBar/app/SearchInput/components/Options/index.tsx @@ -4,7 +4,7 @@ import { Checkbox, Space } from 'antd'; import cls from 'classnames'; import { SearchService } from '../../../useSearchService'; -import { KeyboardService } from '../../useKeyboardService'; +import { KeyboardService } from '../../../useKeyboardService'; import styles from './style.less'; diff --git a/src/contentScripts/searchBar/app/SearchInput/index.tsx b/src/contentScripts/searchBar/app/SearchInput/index.tsx index 5ad91dc..b253047 100644 --- a/src/contentScripts/searchBar/app/SearchInput/index.tsx +++ b/src/contentScripts/searchBar/app/SearchInput/index.tsx @@ -1,9 +1,9 @@ import type { FC } from 'react'; import React, { useContext } from 'react'; import { Input } from 'antd'; -import { KeyboardService, useKeyboardService } from './useKeyboardService'; - import Options from './components/Options'; + +import { KeyboardService } from '../useKeyboardService'; import { SearchService } from '../useSearchService'; import styles from './style.less'; @@ -11,10 +11,10 @@ import styles from './style.less'; const SearchInput: FC = () => { const { onSearchEvent } = useContext(SearchService); - const keyboardService = useKeyboardService(); - const { inputRef, focusOnInput } = keyboardService; + const { inputRef, focusOnInput } = useContext(KeyboardService); + return ( - + <> { size={'large'} onChange={onSearchEvent} onFocus={focusOnInput} - // onKeyDown={onKeyDown} /> - + ); }; diff --git a/src/contentScripts/searchBar/app/SearchResult/index.tsx b/src/contentScripts/searchBar/app/SearchResult/index.tsx index 0e45565..c67a121 100644 --- a/src/contentScripts/searchBar/app/SearchResult/index.tsx +++ b/src/contentScripts/searchBar/app/SearchResult/index.tsx @@ -1,14 +1,19 @@ import React, { useContext } from 'react'; import type { FC } from 'react'; import { Skeleton } from 'antd'; +import cls from 'classnames'; import { SearchService } from '../useSearchService'; +import { KeyboardService } from '../useKeyboardService'; import RepoIcon from './RepoIcon'; import styles from './style.less'; const SearchResult: FC = () => { const { result, loading } = useContext(SearchService); + const { selectResultIndex, handleResultIndex, focusKey } = useContext( + KeyboardService, + ); return ( { active className={styles.skeleton} > - {result?.map((item) => { + {result?.map((item, index) => { const { title, info, id, url, target, type } = item; return (
{ 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 无效,请前往配置页添加 token。 - 添加完成后请刷新页面并重试。 -
- - - + +
+
+ {valid ? ( + <> +
+ +
+
+ + + +
+ + ) : ( + +
+ 无效的 token +
+
+

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; } /**