[WEB-5569] chore: top nav search enhancements (#8226)

* chore: top nav power k search menu enhancements

* chore: expandable search panel refactor
This commit is contained in:
Anmol Singh Bhatia 2025-12-03 16:09:12 +05:30 committed by GitHub
parent b8a41ad5a0
commit 105ac5ece5
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 33 additions and 7 deletions

View file

@ -272,6 +272,7 @@ export const TopNavPowerK = observer(() => {
isWorkspaceLevel={isWorkspaceLevel}
searchTerm={searchTerm}
setSearchTerm={setSearchTerm}
handleSearchMenuClose={() => closePanel()}
/>
</Command.List>
<PowerKModalFooter

View file

@ -11,6 +11,7 @@ export type TPowerKCommandsListProps = {
isWorkspaceLevel: boolean;
searchTerm: string;
setSearchTerm: (value: string) => void;
handleSearchMenuClose?: () => void;
};
export function ProjectsAppPowerKCommandsList(props: TPowerKCommandsListProps) {
@ -22,6 +23,7 @@ export function ProjectsAppPowerKCommandsList(props: TPowerKCommandsListProps) {
isWorkspaceLevel,
searchTerm,
setSearchTerm,
handleSearchMenuClose,
} = props;
return (
@ -32,6 +34,7 @@ export function ProjectsAppPowerKCommandsList(props: TPowerKCommandsListProps) {
isWorkspaceLevel={!context.params.projectId || isWorkspaceLevel}
searchTerm={searchTerm}
updateSearchTerm={setSearchTerm}
handleSearchMenuClose={handleSearchMenuClose}
/>
<PowerKContextBasedPagesList
activeContext={context.activeContext}

View file

@ -22,10 +22,11 @@ type Props = {
isWorkspaceLevel: boolean;
searchTerm: string;
updateSearchTerm: (value: string) => void;
handleSearchMenuClose?: () => void;
};
export function PowerKModalSearchMenu(props: Props) {
const { activePage, context, isWorkspaceLevel, searchTerm, updateSearchTerm } = props;
const { activePage, context, isWorkspaceLevel, searchTerm, updateSearchTerm, handleSearchMenuClose } = props;
// states
const [resultsCount, setResultsCount] = useState(0);
const [isSearching, setIsSearching] = useState(false);
@ -68,6 +69,11 @@ export function PowerKModalSearchMenu(props: Props) {
if (activePage) return null;
const handleClosePalette = () => {
handleSearchMenuClose?.();
togglePowerKModal(false);
};
return (
<>
{searchTerm.trim() !== "" && (
@ -97,9 +103,7 @@ export function PowerKModalSearchMenu(props: Props) {
/>
)}
{searchTerm.trim() !== "" && (
<PowerKModalSearchResults closePalette={() => togglePowerKModal(false)} results={results} />
)}
{searchTerm.trim() !== "" && <PowerKModalSearchResults closePalette={handleClosePalette} results={results} />}
</>
);
}

View file

@ -1,4 +1,4 @@
import { useCallback, useRef, useState } from "react";
import { useCallback, useEffect, useRef, useState } from "react";
import { useOutsideClickDetector } from "@plane/hooks";
type UseExpandableSearchOptions = {
@ -8,6 +8,7 @@ type UseExpandableSearchOptions = {
/**
* Custom hook for expandable search input behavior
* Handles focus management to prevent unwanted opening on programmatic focus restoration
* Opens on click, typing, or keyboard shortcut (via PowerK Cmd+F)
*/
export const useExpandableSearch = (options?: UseExpandableSearchOptions) => {
const { onClose } = options || {};
@ -19,6 +20,7 @@ export const useExpandableSearch = (options?: UseExpandableSearchOptions) => {
const containerRef = useRef<HTMLDivElement>(null);
const inputRef = useRef<HTMLInputElement>(null);
const wasClickedRef = useRef<boolean>(false);
const wasKeyboardTriggeredRef = useRef<boolean>(false);
// Handle close
const handleClose = useCallback(() => {
@ -37,16 +39,32 @@ export const useExpandableSearch = (options?: UseExpandableSearchOptions) => {
// Outside click detection
useOutsideClickDetector(containerRef, handleOutsideClick);
// Track keyboard shortcuts that trigger focus (Cmd+F / Ctrl+F)
useEffect(() => {
const handleKeyDown = (e: KeyboardEvent) => {
if ((e.metaKey || e.ctrlKey) && e.key === "f") {
// Mark as keyboard triggered so handleFocus knows to open
wasKeyboardTriggeredRef.current = true;
}
};
document.addEventListener("keydown", handleKeyDown);
return () => {
document.removeEventListener("keydown", handleKeyDown);
};
}, []);
// Track explicit clicks
const handleMouseDown = useCallback(() => {
wasClickedRef.current = true;
}, []);
// Only open on explicit clicks, not programmatic focus
// Open on explicit clicks or keyboard shortcut, not programmatic focus restoration
const handleFocus = useCallback(() => {
if (wasClickedRef.current) {
if (wasClickedRef.current || wasKeyboardTriggeredRef.current) {
setIsOpen(true);
wasClickedRef.current = false;
wasKeyboardTriggeredRef.current = false;
}
}, []);