diff --git a/apps/roam/src/components/AdvancedNodeSearchDialog/AdvancedSearchDialog.tsx b/apps/roam/src/components/AdvancedNodeSearchDialog/AdvancedSearchDialog.tsx index a5d6bbdd6..ac2da0d2f 100644 --- a/apps/roam/src/components/AdvancedNodeSearchDialog/AdvancedSearchDialog.tsx +++ b/apps/roam/src/components/AdvancedNodeSearchDialog/AdvancedSearchDialog.tsx @@ -1,14 +1,8 @@ -import React, { - useCallback, - useEffect, - useMemo, - useRef, - useState, -} from "react"; +import React, { useCallback, useEffect, useRef, useState } from "react"; import { Button, Dialog, - InputGroup, + Icon, NonIdealState, Spinner, SpinnerSize, @@ -34,6 +28,7 @@ import { getNodeTagStyles } from "~/utils/getDiscourseNodeColors"; import { DEBOUNCE_MS, DEFAULT_SORT_CONFIG, + MAX_RESULTS, type SearchResult, type SortConfig, buildSearchIndex, @@ -46,6 +41,7 @@ import { import { DiscourseNodeTypeFilter } from "~/components/AdvancedNodeSearchDialog/DiscourseNodeTypeFilter"; import { RenderRoamBlock, RenderRoamPage } from "~/utils/roamReactComponents"; import { AdvancedSearchFooter } from "./AdvancedSearchFooter"; +import { NodeTypeChipsSearchInput } from "./NodeTypeChipsSearchInput"; type Props = Record; @@ -160,6 +156,7 @@ const AdvancedNodeSearchDialog = ({ const [sort, setSort] = useState(DEFAULT_SORT_CONFIG); const [discourseNodes, setDiscourseNodes] = useState([]); const [selectedNodeTypeIds, setSelectedNodeTypeIds] = useState([]); + const [isTypeFilterPopoverOpen, setIsTypeFilterPopoverOpen] = useState(false); const miniSearchRef = useRef | null>(null); @@ -205,25 +202,31 @@ const AdvancedNodeSearchDialog = ({ }, [isOpen]); useEffect(() => { - if ( - !isOpen || - isIndexLoading || - indexError || - !debouncedSearchTerm || - !miniSearchRef.current - ) { + const hasTypeFilters = selectedNodeTypeIds.length > 0; + + if (!isOpen || isIndexLoading || indexError || !miniSearchRef.current) { setResults([]); return; } - const scoredHits = searchIndexedNodes({ - miniSearch: miniSearchRef.current, - allResults: allResultsRef.current, - searchTerm: debouncedSearchTerm, - typeFilter: selectedNodeTypeIds.length ? selectedNodeTypeIds : undefined, - }); + if (!debouncedSearchTerm && !hasTypeFilters) { + setResults([]); + return; + } - setResults(sortSearchResults({ hits: scoredHits, sort })); + const scoredHits = debouncedSearchTerm + ? searchIndexedNodes({ + miniSearch: miniSearchRef.current, + allResults: allResultsRef.current, + searchTerm: debouncedSearchTerm, + typeFilter: hasTypeFilters ? selectedNodeTypeIds : undefined, + }) + : allResultsRef.current + .filter((result) => selectedNodeTypeIds.includes(result.type)) + .map((result) => ({ result, score: 1 })); + + const sortedResults = sortSearchResults({ hits: scoredHits, sort }); + setResults(sortedResults.slice(0, MAX_RESULTS)); }, [ debouncedSearchTerm, indexError, @@ -314,7 +317,7 @@ const AdvancedNodeSearchDialog = ({ ? "error" : isIndexLoading ? "indexing" - : !debouncedSearchTerm + : !debouncedSearchTerm && selectedNodeTypeIds.length === 0 ? "initial" : !results.length ? "empty" @@ -351,6 +354,7 @@ const AdvancedNodeSearchDialog = ({ const onKeyDown = useCallback( (event: React.KeyboardEvent) => { + if (event.defaultPrevented) return; if (event.key === "ArrowDown" && results.length) { event.preventDefault(); setActiveIndex((index) => Math.min(index + 1, results.length - 1)); @@ -377,6 +381,7 @@ const AdvancedNodeSearchDialog = ({ event.preventDefault(); void onInsert(); } else if (event.key === "Escape") { + if (isTypeFilterPopoverOpen) return; event.preventDefault(); onClose(); } @@ -384,6 +389,7 @@ const AdvancedNodeSearchDialog = ({ [ activeResult, contentState, + isTypeFilterPopoverOpen, insertTarget, onClose, onInsert, @@ -416,29 +422,50 @@ const AdvancedNodeSearchDialog = ({ onMouseUp={(event) => event.stopPropagation()} className="flex min-h-0 flex-1 flex-col overflow-hidden" > -
- ) => - setSearchTerm(event.target.value) - } - placeholder="Search discourse nodes..." - value={searchTerm} - /> - - +
+
+ + + setActiveIndex((index) => + Math.min(index + 1, results.length - 1), + ) + } + onArrowUp={() => + setActiveIndex((index) => Math.max(index - 1, 0)) + } + onCmdEnter={() => void onInsert()} + onEnter={() => void onOpen()} + onEscape={() => { + if (isTypeFilterPopoverOpen) return; + onClose(); + }} + onSearchTermChange={setSearchTerm} + onSelectedTypeIdsChange={setSelectedNodeTypeIds} + onShiftEnter={() => void onOpenInSidebar()} + searchTerm={searchTerm} + selectedTypeIds={selectedNodeTypeIds} + /> +
+
+ +
+
+ +
+ ); +};