feat: support pageup/pagedown to navigate search results (#920)

* feat: support pageup/pagedown to navigate search results

* docs: add release note
This commit is contained in:
BiggerRain
2025-10-11 17:08:53 +08:00
committed by GitHub
parent 95dc7a88d2
commit 00eb6bed2b
4 changed files with 59 additions and 9 deletions

View File

@@ -16,6 +16,7 @@ Information about release notes of Coco App is provided here.
feat: support switching groups via keyboard shortcuts #911 feat: support switching groups via keyboard shortcuts #911
feat: support opening logs from about page #915 feat: support opening logs from about page #915
feat: support moving cursor with home and end keys #918 feat: support moving cursor with home and end keys #918
feat: support pageup/pagedown to navigate search results #920
### 🐛 Bug fix ### 🐛 Bug fix

View File

@@ -0,0 +1,47 @@
import { useEventListener } from "ahooks";
import clsx from "clsx";
import {
forwardRef,
HTMLAttributes,
useImperativeHandle,
useRef,
} from "react";
const Scrollbar = forwardRef<HTMLDivElement, HTMLAttributes<HTMLDivElement>>(
(props, ref) => {
const { children, className, ...rest } = props;
const containerRef = useRef<HTMLDivElement>(null);
useImperativeHandle(ref, () => containerRef.current as HTMLDivElement);
useEventListener("keydown", (event) => {
const { key } = event;
if (key !== "PageDown" && key !== "PageUp") return;
if (!containerRef.current) return;
event.preventDefault();
const delta = key === "PageDown" ? 1 : -1;
const el = containerRef.current;
el.scrollBy({
top: delta * el.clientHeight * 0.9,
behavior: "smooth",
});
});
return (
<div
{...rest}
ref={containerRef}
className={clsx("custom-scrollbar", className)}
>
{children}
</div>
);
}
);
export default Scrollbar;

View File

@@ -1,6 +1,9 @@
import React, { useState, useRef, useEffect, useCallback } from "react"; import React, { useState, useRef, useEffect, useCallback } from "react";
import { useInfiniteScroll } from "ahooks"; import { useInfiniteScroll } from "ahooks";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import { Data } from "ahooks/lib/useInfiniteScroll/types";
import { nanoid } from "nanoid";
import { isNil } from "lodash-es";
import { useSearchStore } from "@/stores/searchStore"; import { useSearchStore } from "@/stores/searchStore";
import { SearchHeader } from "./SearchHeader"; import { SearchHeader } from "./SearchHeader";
@@ -11,9 +14,7 @@ import { Get } from "@/api/axiosRequest";
import { useAppStore } from "@/stores/appStore"; import { useAppStore } from "@/stores/appStore";
import { useConnectStore } from "@/stores/connectStore"; import { useConnectStore } from "@/stores/connectStore";
import SearchEmpty from "../Common/SearchEmpty"; import SearchEmpty from "../Common/SearchEmpty";
import { Data } from "ahooks/lib/useInfiniteScroll/types"; import Scrollbar from "@/components/Common/Scrollbar";
import { nanoid } from "nanoid";
import { isNil } from "lodash-es";
interface DocumentListProps { interface DocumentListProps {
onSelectDocument: (id: string) => void; onSelectDocument: (id: string) => void;
@@ -297,8 +298,8 @@ export const DocumentList: React.FC<DocumentListProps> = ({
/> />
</div> </div>
<div <Scrollbar
className="flex-1 overflow-auto custom-scrollbar pr-0.5" className="flex-1 overflow-auto pr-0.5"
ref={containerRef} ref={containerRef}
> >
{data?.list && data.list.length > 0 && ( {data?.list && data.list.length > 0 && (
@@ -334,7 +335,7 @@ export const DocumentList: React.FC<DocumentListProps> = ({
<SearchEmpty /> <SearchEmpty />
</div> </div>
)} )}
</div> </Scrollbar>
</div> </div>
); );
}; };

View File

@@ -16,6 +16,7 @@ import { useKeyboardNavigation } from "@/hooks/useKeyboardNavigation";
import { SearchSource } from "./SearchSource"; import { SearchSource } from "./SearchSource";
import DropdownListItem from "./DropdownListItem"; import DropdownListItem from "./DropdownListItem";
import platformAdapter from "@/utils/platformAdapter"; import platformAdapter from "@/utils/platformAdapter";
import Scrollbar from "@/components/Common/Scrollbar";
type ISearchData = Record<string, QueryHits[]>; type ISearchData = Record<string, QueryHits[]>;
@@ -149,10 +150,10 @@ function DropdownList({
}); });
return ( return (
<div <Scrollbar
ref={containerRef} ref={containerRef}
data-tauri-drag-region data-tauri-drag-region
className="h-full w-full p-2 flex flex-col overflow-y-auto custom-scrollbar focus:outline-none" className="h-full w-full p-2 flex flex-col overflow-y-auto focus:outline-none"
tabIndex={0} tabIndex={0}
role="listbox" role="listbox"
aria-label={t("search.header.results")} aria-label={t("search.header.results")}
@@ -189,7 +190,7 @@ function DropdownList({
})} })}
</div> </div>
))} ))}
</div> </Scrollbar>
); );
} }