11import { Fragment , h } from 'preact' ;
22import { eventToTarget } from '../../../../../shared/handlers' ;
33import {
4+ AiChatIcon ,
45 ArrowRightIcon ,
56 BookmarkIcon ,
67 BrowserIcon ,
@@ -15,8 +16,10 @@ import { getSuggestionSuffix, getSuggestionTitle, startsWithIgnoreCase } from '.
1516import { useSearchFormContext } from './SearchFormProvider' ;
1617import { SuffixText } from './SuffixText' ;
1718import styles from './SuggestionsList.module.css' ;
19+ import { useTypedTranslationWith } from '../../types' ;
1820
1921/**
22+ * @typedef {import('../strings.json') } Strings
2023 * @typedef {import('./useSuggestions').SuggestionModel } SuggestionModel
2124 * @typedef {import('../../../types/new-tab.js').Suggestion } Suggestion
2225 * @typedef {import('../../../types/new-tab.js').OpenTarget } OpenTarget
@@ -25,63 +28,104 @@ import styles from './SuggestionsList.module.css';
2528/**
2629 * @param {object } props
2730 * @param {(params: {suggestion: Suggestion, target: OpenTarget}) => void } props.onOpenSuggestion
31+ * @param {(params: {chat: string, target: OpenTarget}) => void } props.onSubmitChat
2832 */
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 ( ) ;
3435
3536 if ( suggestions . length === 0 ) return null ;
3637
38+ const mainSuggestions = suggestions . filter ( ( suggestion ) => suggestion . kind !== 'aiChat' ) ;
39+ const footerSuggestions = suggestions . filter ( ( suggestion ) => suggestion . kind === 'aiChat' ) ;
40+
3741 return (
3842 < 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+ ) }
8167 </ div >
8268 ) ;
8369}
8470
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+
85129/**
86130 * @param {object } props
87131 * @param {SuggestionModel } props.suggestion
@@ -100,6 +144,8 @@ function SuggestionIcon({ suggestion }) {
100144 return < TabDesktopIcon /> ;
101145 case 'internalPage' :
102146 return < BrowserIcon /> ;
147+ case 'aiChat' :
148+ return < AiChatIcon /> ;
103149 default :
104150 throw new Error ( 'Unknown suggestion kind' ) ;
105151 }
0 commit comments