@@ -17,6 +17,7 @@ import {
1717 TagsListItem ,
1818 type SharedProps ,
1919} from "fumadocs-ui/components/dialog/search" ;
20+ import { useRouter } from "next/navigation" ;
2021
2122interface TagItem {
2223 name : string ;
@@ -34,6 +35,12 @@ interface DefaultSearchDialogProps extends SharedProps {
3435 allowClear ?: boolean ;
3536}
3637
38+ interface SearchItem {
39+ url ?: string ;
40+ onSelect ?: ( value : string ) => void ;
41+ [ key : string ] : unknown ;
42+ }
43+
3744export function CustomSearchDialog ( {
3845 defaultTag,
3946 tags = [ ] ,
@@ -46,7 +53,12 @@ export function CustomSearchDialog({
4653 ...props
4754} : DefaultSearchDialogProps ) {
4855 const { locale } = useI18n ( ) ;
56+ const router = useRouter ( ) ;
4957 const [ tag , setTag ] = useState ( defaultTag ) ;
58+
59+ // Extract onOpenChange to use in dependency array cleanly
60+ const { onOpenChange, ...otherProps } = props ;
61+
5062 const { search, setSearch, query } = useDocsSearch (
5163 type === "fetch"
5264 ? {
@@ -65,11 +77,12 @@ export function CustomSearchDialog({
6577 } ,
6678 ) ;
6779
68- // Tracking logic
80+ // Tracking logic for queries
6981 useEffect ( ( ) => {
7082 if ( ! search ) return ;
7183
7284 const timer = setTimeout ( ( ) => {
85+ // Umami 埋点: 搜索结果点击
7386 if ( window . umami ) {
7487 window . umami . track ( "search_query" , { query : search } ) ;
7588 }
@@ -88,12 +101,48 @@ export function CustomSearchDialog({
88101 } ) ) ;
89102 } , [ links ] ) ;
90103
104+ // 使用 useMemo 劫持 search items,注入埋点逻辑
105+ const trackedItems = useMemo ( ( ) => {
106+ const data = query . data !== "empty" && query . data ? query . data : defaultItems ;
107+ if ( ! data ) return [ ] ;
108+
109+ return data . map ( ( item : unknown , index : number ) => {
110+ const searchItem = item as SearchItem ;
111+ return {
112+ ...searchItem ,
113+ onSelect : ( value : string ) => {
114+ // Umami 埋点: 搜索结果点击
115+ if ( window . umami ) {
116+ window . umami . track ( "search_result_click" , {
117+ query : search ,
118+ rank : index + 1 ,
119+ url : searchItem . url ,
120+ } ) ;
121+ }
122+
123+ // Call original onSelect if it exists
124+ if ( searchItem . onSelect ) searchItem . onSelect ( value ) ;
125+
126+ // Handle navigation if URL exists
127+ if ( searchItem . url ) {
128+ // 显式执行路由跳转和关闭弹窗,确保点击行为能够同时触发埋点和导航
129+ router . push ( searchItem . url ) ;
130+ if ( onOpenChange ) {
131+ onOpenChange ( false ) ;
132+ }
133+ }
134+ } ,
135+ } ;
136+ } ) ;
137+ } , [ query . data , defaultItems , search , router , onOpenChange ] ) ;
138+
91139 return (
92140 < SearchDialog
93141 search = { search }
94142 onSearchChange = { setSearch }
95143 isLoading = { query . isLoading }
96- { ...props }
144+ onOpenChange = { onOpenChange }
145+ { ...otherProps }
97146 >
98147 < SearchDialogOverlay />
99148 < SearchDialogContent >
@@ -102,11 +151,8 @@ export function CustomSearchDialog({
102151 < SearchDialogInput />
103152 < SearchDialogClose />
104153 </ SearchDialogHeader >
105- < SearchDialogList
106- items = {
107- query . data !== "empty" && query . data ? query . data : defaultItems
108- }
109- />
154+ { /* eslint-disable-next-line @typescript-eslint/no-explicit-any */ }
155+ < SearchDialogList items = { trackedItems as any } />
110156 </ SearchDialogContent >
111157 < SearchDialogFooter >
112158 { tags . length > 0 && (
0 commit comments