1
1
import { Fragment , h } from 'preact' ;
2
2
import { eventToTarget } from '../../../../../shared/handlers' ;
3
3
import {
4
+ AiChatIcon ,
4
5
ArrowRightIcon ,
5
6
BookmarkIcon ,
6
7
BrowserIcon ,
@@ -15,8 +16,10 @@ import { getSuggestionSuffix, getSuggestionTitle, startsWithIgnoreCase } from '.
15
16
import { useSearchFormContext } from './SearchFormProvider' ;
16
17
import { SuffixText } from './SuffixText' ;
17
18
import styles from './SuggestionsList.module.css' ;
19
+ import { useTypedTranslationWith } from '../../types' ;
18
20
19
21
/**
22
+ * @typedef {import('../strings.json') } Strings
20
23
* @typedef {import('./useSuggestions').SuggestionModel } SuggestionModel
21
24
* @typedef {import('../../../types/new-tab.js').Suggestion } Suggestion
22
25
* @typedef {import('../../../types/new-tab.js').OpenTarget } OpenTarget
@@ -25,63 +28,104 @@ import styles from './SuggestionsList.module.css';
25
28
/**
26
29
* @param {object } props
27
30
* @param {(params: {suggestion: Suggestion, target: OpenTarget}) => void } props.onOpenSuggestion
31
+ * @param {(params: {chat: string, target: OpenTarget}) => void } props.onSubmitChat
28
32
*/
29
- export function SuggestionsList ( { onOpenSuggestion } ) {
30
- const platformName = usePlatformName ( ) ;
31
-
32
- const { term, suggestionsListId, suggestions, selectedSuggestion, setSelectedSuggestion, clearSelectedSuggestion } =
33
- useSearchFormContext ( ) ;
33
+ export function SuggestionsList ( { onOpenSuggestion, onSubmitChat } ) {
34
+ const { suggestionsListId, suggestions } = useSearchFormContext ( ) ;
34
35
35
36
if ( suggestions . length === 0 ) return null ;
36
37
38
+ const mainSuggestions = suggestions . filter ( ( suggestion ) => suggestion . kind !== 'aiChat' ) ;
39
+ const footerSuggestions = suggestions . filter ( ( suggestion ) => suggestion . kind === 'aiChat' ) ;
40
+
37
41
return (
38
42
< div role = "listbox" id = { suggestionsListId } class = { styles . list } >
39
- { suggestions . map ( ( suggestion ) => {
40
- const title = getSuggestionTitle ( suggestion , term ) ;
41
- const suffix = getSuggestionSuffix ( suggestion ) ;
42
- return (
43
- < button
44
- key = { suggestion . id }
45
- role = "option"
46
- id = { suggestion . id }
47
- class = { styles . item }
48
- tabIndex = { suggestion === selectedSuggestion ? 0 : - 1 }
49
- aria-selected = { suggestion === selectedSuggestion }
50
- onMouseOver = { ( ) => setSelectedSuggestion ( suggestion ) }
51
- onMouseLeave = { ( ) => clearSelectedSuggestion ( ) }
52
- onClick = { ( event ) => {
53
- event . preventDefault ( ) ;
54
- onOpenSuggestion ( { suggestion, target : eventToTarget ( event , platformName ) } ) ;
55
- } }
56
- >
57
- < SuggestionIcon suggestion = { suggestion } />
58
- < span class = { styles . title } >
59
- { startsWithIgnoreCase ( title , term ) ? (
60
- < >
61
- < b > { title . slice ( 0 , term . length ) } </ b >
62
- { title . slice ( term . length ) }
63
- </ >
64
- ) : (
65
- title
66
- ) }
67
- </ span >
68
- { suffix && (
69
- < span class = { styles . suffix } >
70
- < SuffixText suffix = { suffix } />
71
- </ span >
72
- ) }
73
- { suggestion . kind === 'openTab' && (
74
- < span class = { styles . badge } >
75
- Switch to Tab < ArrowRightIcon />
76
- </ span >
77
- ) }
78
- </ button >
79
- ) ;
80
- } ) }
43
+ { mainSuggestions . length > 0 && (
44
+ < div class = { styles . main } >
45
+ { mainSuggestions . map ( ( suggestion ) => (
46
+ < SuggestionsListItem
47
+ key = { suggestion . id }
48
+ suggestion = { suggestion }
49
+ onOpenSuggestion = { onOpenSuggestion }
50
+ onSubmitChat = { onSubmitChat }
51
+ />
52
+ ) ) }
53
+ </ div >
54
+ ) }
55
+ { footerSuggestions . length > 0 && (
56
+ < div class = { styles . footer } >
57
+ { footerSuggestions . map ( ( suggestion ) => (
58
+ < SuggestionsListItem
59
+ key = { suggestion . id }
60
+ suggestion = { suggestion }
61
+ onOpenSuggestion = { onOpenSuggestion }
62
+ onSubmitChat = { onSubmitChat }
63
+ />
64
+ ) ) }
65
+ </ div >
66
+ ) }
81
67
</ div >
82
68
) ;
83
69
}
84
70
71
+ /**
72
+ * @param {object } props
73
+ * @param {SuggestionModel } props.suggestion
74
+ * @param {(params: {suggestion: Suggestion, target: OpenTarget}) => void } props.onOpenSuggestion
75
+ * @param {(params: {chat: string, target: OpenTarget}) => void } props.onSubmitChat
76
+ */
77
+ function SuggestionsListItem ( { suggestion, onOpenSuggestion, onSubmitChat } ) {
78
+ const { t } = useTypedTranslationWith ( /** @type {Strings } */ ( { } ) ) ;
79
+ const platformName = usePlatformName ( ) ;
80
+
81
+ const { term, selectedSuggestion, setSelectedSuggestion, clearSelectedSuggestion } = useSearchFormContext ( ) ;
82
+
83
+ const title = getSuggestionTitle ( suggestion , term ) ;
84
+ const suffix = getSuggestionSuffix ( suggestion ) ;
85
+
86
+ return (
87
+ < button
88
+ role = "option"
89
+ id = { suggestion . id }
90
+ class = { styles . item }
91
+ tabIndex = { suggestion === selectedSuggestion ? 0 : - 1 }
92
+ aria-selected = { suggestion === selectedSuggestion }
93
+ onMouseOver = { ( ) => setSelectedSuggestion ( suggestion ) }
94
+ onMouseLeave = { ( ) => clearSelectedSuggestion ( ) }
95
+ onClick = { ( event ) => {
96
+ event . preventDefault ( ) ;
97
+ if ( suggestion . kind === 'aiChat' ) {
98
+ onSubmitChat ( { chat : suggestion . chat , target : eventToTarget ( event , platformName ) } ) ;
99
+ } else {
100
+ onOpenSuggestion ( { suggestion, target : eventToTarget ( event , platformName ) } ) ;
101
+ }
102
+ } }
103
+ >
104
+ < SuggestionIcon suggestion = { suggestion } />
105
+ < span class = { styles . title } >
106
+ { startsWithIgnoreCase ( title , term ) ? (
107
+ < >
108
+ < b > { title . slice ( 0 , term . length ) } </ b >
109
+ { title . slice ( term . length ) }
110
+ </ >
111
+ ) : (
112
+ title
113
+ ) }
114
+ </ span >
115
+ { suffix && (
116
+ < span class = { styles . suffix } >
117
+ < SuffixText suffix = { suffix } />
118
+ </ span >
119
+ ) }
120
+ { suggestion . kind === 'openTab' && (
121
+ < span class = { styles . badge } >
122
+ { t ( 'omnibar_switchToTab' ) } < ArrowRightIcon />
123
+ </ span >
124
+ ) }
125
+ </ button >
126
+ ) ;
127
+ }
128
+
85
129
/**
86
130
* @param {object } props
87
131
* @param {SuggestionModel } props.suggestion
@@ -100,6 +144,8 @@ function SuggestionIcon({ suggestion }) {
100
144
return < TabDesktopIcon /> ;
101
145
case 'internalPage' :
102
146
return < BrowserIcon /> ;
147
+ case 'aiChat' :
148
+ return < AiChatIcon /> ;
103
149
default :
104
150
throw new Error ( 'Unknown suggestion kind' ) ;
105
151
}
0 commit comments