diff --git a/src/contentScripts/searchBar/app/SearchInput/components/Options/index.tsx b/src/contentScripts/searchBar/app/SearchInput/components/Options/index.tsx
index f8e9ad2..f2f9768 100644
--- a/src/contentScripts/searchBar/app/SearchInput/components/Options/index.tsx
+++ b/src/contentScripts/searchBar/app/SearchInput/components/Options/index.tsx
@@ -4,39 +4,36 @@ import { Checkbox, Space } from 'antd';
import cls from 'classnames';
import { SearchService } from '../../../useSearchService';
+import { KeyboardService } from '../../useKeyboardService';
import styles from './style.less';
-interface Option {
- key: SearchBar.SearchType;
- title: string;
-}
-
-const options: Option[] = [
- { key: 'repo', title: '知识库' },
- { key: 'doc', title: '文档' },
- { key: 'topic', title: '主题' },
- { key: 'artboard', title: '画板' },
- { key: 'group', title: '团队' },
- // { key: 'user', title: '用户' },
- // { key: 'attachment', title: '附件' },
-];
-
const Options: FC = () => {
- const { type, setType, related, setRelated } = useContext(SearchService);
+ const {
+ type,
+ setType,
+ related,
+ setRelated,
+ options,
+ optionActiveIndex,
+ } = useContext(SearchService);
+ const { focusKey, focusOnOptions } = useContext(KeyboardService);
return (
- {options.map((option) => (
+ {options.map((option, index) => (
{
setType(option.key);
+ focusOnOptions();
}}
>
{option.title}
diff --git a/src/contentScripts/searchBar/app/SearchInput/components/Options/style.less b/src/contentScripts/searchBar/app/SearchInput/components/Options/style.less
index b23565b..c170464 100644
--- a/src/contentScripts/searchBar/app/SearchInput/components/Options/style.less
+++ b/src/contentScripts/searchBar/app/SearchInput/components/Options/style.less
@@ -15,17 +15,27 @@
transition: all 150ms ease-in-out;
}
-.optionActive {
+.active {
color: @yuque-brand-color-dark;
background: @yuque-brand-color-light;
}
+.selected {
+ color: white;
+ background: @yuque-brand-color-light-darken;
+}
+
[theme='dark'] {
.option {
background: @dark-mode-background-dark;
}
- .optionActive {
+ .active {
color: @dark-mode-yuque-brand-color-dark;
background: @dark-mode-yuque-brand-color-light;
}
+
+ .selected {
+ color: white;
+ background: @dark-mode-yuque-brand-color-light-lighter;
+ }
}
diff --git a/src/contentScripts/searchBar/app/SearchInput/index.tsx b/src/contentScripts/searchBar/app/SearchInput/index.tsx
index c37b2ea..5ad91dc 100644
--- a/src/contentScripts/searchBar/app/SearchInput/index.tsx
+++ b/src/contentScripts/searchBar/app/SearchInput/index.tsx
@@ -1,32 +1,32 @@
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 { SearchService } from '../useSearchService';
-import { SearchBarService } from '../useSearchBarService';
+
import styles from './style.less';
const SearchInput: FC = () => {
const { onSearchEvent } = useContext(SearchService);
- const { hide } = useContext(SearchBarService);
+ const keyboardService = useKeyboardService();
+ const { inputRef, focusOnInput } = keyboardService;
return (
-
+
{
- if (e.key === 'Escape') {
- hide();
- }
- }}
+ onFocus={focusOnInput}
+ // onKeyDown={onKeyDown}
/>
-
+
);
};
diff --git a/src/contentScripts/searchBar/app/SearchInput/useKeyboardService.ts b/src/contentScripts/searchBar/app/SearchInput/useKeyboardService.ts
new file mode 100644
index 0000000..b96a9e8
--- /dev/null
+++ b/src/contentScripts/searchBar/app/SearchInput/useKeyboardService.ts
@@ -0,0 +1,110 @@
+import { useContext, useEffect, useRef, useState } from 'react';
+import type { Input } from 'antd';
+
+import { getServiceToken } from '@/utils';
+import { SearchService } from '../useSearchService';
+import { SearchBarService } from '../useSearchBarService';
+
+type FocusType = 'input' | 'options' | 'result';
+
+/**
+ * Keyboard 需要的状态
+ */
+export const useKeyboardService = () => {
+ const { setType, optionKeys, optionActiveIndex } = useContext(SearchService);
+ const { hide } = useContext(SearchBarService);
+
+ const [focusKey, setFocusKey] = useState
('input');
+ const inputRef = useRef(null);
+
+ /**
+ * 按 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;
+ }
+
+ setType(optionKeys[newIndex]);
+ };
+
+ const focusOnInput = () => {
+ inputRef.current?.focus();
+ setFocusKey('input');
+ };
+
+ const focusOnOptions = () => {
+ inputRef.current?.blur();
+ setFocusKey('options');
+ };
+
+ // 将焦点切换到 Options
+ const onKeyDown = (event: KeyboardEvent) => {
+ // 焦点在 options 的情况
+ if (focusKey === 'options') {
+ console.log(event.key);
+ switch (event.key) {
+ case 'Tab':
+ event.preventDefault();
+ switchOptionIndex(event.shiftKey);
+ break;
+
+ case 'ArrowRight':
+ switchOptionIndex();
+ break;
+ case 'ArrowLeft':
+ switchOptionIndex(true);
+ break;
+ case 'ArrowUp':
+ case 'Escape':
+ focusOnInput();
+ break;
+ default:
+ }
+ }
+
+ // 焦点在 input 的情况
+ if (focusKey === 'input') {
+ switch (event.key) {
+ case 'Tab':
+ event.preventDefault();
+ focusOnOptions();
+ switchOptionIndex(event.shiftKey);
+ break;
+ case 'Escape':
+ hide();
+ break;
+ case 'ArrowDown':
+ event.preventDefault();
+ focusOnOptions();
+ break;
+ default:
+ }
+ }
+ };
+
+ useEffect(() => {
+ window.onkeydown = onKeyDown;
+
+ return () => {
+ window.onkeydown = null;
+ };
+ }, [focusKey, optionActiveIndex]);
+
+ return {
+ onKeyDown,
+ focusKey,
+ inputRef,
+ focusOnInput,
+ focusOnOptions,
+ };
+};
+
+export const KeyboardService = getServiceToken(useKeyboardService);
diff --git a/src/contentScripts/searchBar/app/SearchResult/index.tsx b/src/contentScripts/searchBar/app/SearchResult/index.tsx
index b6f5ab6..0e45565 100644
--- a/src/contentScripts/searchBar/app/SearchResult/index.tsx
+++ b/src/contentScripts/searchBar/app/SearchResult/index.tsx
@@ -11,7 +11,15 @@ const SearchResult: FC = () => {
const { result, loading } = useContext(SearchService);
return (
-
+
{result?.map((item) => {
const { title, info, id, url, target, type } = item;
diff --git a/src/contentScripts/searchBar/app/SearchResult/style.less b/src/contentScripts/searchBar/app/SearchResult/style.less
index 49c4c69..7d43409 100644
--- a/src/contentScripts/searchBar/app/SearchResult/style.less
+++ b/src/contentScripts/searchBar/app/SearchResult/style.less
@@ -1,5 +1,6 @@
@import '~theme/index';
.skeleton {
+ margin-top: 16px;
padding: 8px 12px 24px;
}
.repo {
diff --git a/src/contentScripts/searchBar/app/index.tsx b/src/contentScripts/searchBar/app/index.tsx
index d6d5f33..49113d3 100644
--- a/src/contentScripts/searchBar/app/index.tsx
+++ b/src/contentScripts/searchBar/app/index.tsx
@@ -12,6 +12,7 @@ import SearchResult from './SearchResult';
import AnimatedHeight from './AnimatedHeight';
import styles from './style.less';
+import { isDev } from '@/utils';
const SearchBar: FC = () => {
const { visible, searchBarRef } = useContext(SearchBarService);
@@ -60,7 +61,6 @@ const SearchBar: FC = () => {