1
1
import { useTranslation } from 'react-i18next'
2
- import { MenuItem , FormControl , Select , SelectChangeEvent , Typography } from '@mui/material'
2
+ import { MenuItem , FormControl , Select , SelectChangeEvent , Typography , Tooltip } from '@mui/material'
3
3
import { FREE_MODEL } from '../../../config'
4
+ import { KeyCombinations } from './useKeyboardCommands'
5
+ import React from 'react'
4
6
5
7
const ModelSelector = ( {
6
8
currentModel,
7
9
setModel,
8
10
availableModels,
9
11
isTokenLimitExceeded,
12
+ isOpen,
13
+ setIsOpen,
10
14
} : {
11
15
currentModel : string
12
16
setModel : ( model : string ) => void
13
17
availableModels : string [ ]
14
18
isTokenLimitExceeded : boolean
19
+ isOpen : boolean
20
+ setIsOpen : ( isOpen : boolean ) => void
15
21
} ) => {
16
22
const { t } = useTranslation ( )
23
+ const selectRef = React . useRef < HTMLSelectElement > ( null )
17
24
25
+ /**
26
+ * Extra tooltip logic because kb shortcut focusing would leave the tooltip annoyingly open without this.
27
+ */
28
+ const [ tooltipOpen , setTooltipOpen ] = React . useState ( false )
18
29
const validModel = availableModels . includes ( currentModel ) ? currentModel : ''
19
30
20
31
return (
@@ -26,43 +37,76 @@ const ModelSelector = ({
26
37
} }
27
38
size = "small"
28
39
>
29
- < Select
30
- sx = { {
31
- '& .MuiOutlinedInput-notchedOutline' : {
32
- border : 'none' ,
33
- } ,
34
- '& .MuiSelect-select' : {
35
- overflow : 'hidden' ,
36
- whiteSpace : 'nowrap' ,
37
- paddingLeft : { xs : '4px !important' , md : '10px !important' } ,
38
- paddingRight : { xs : '20px !important' , md : '30px !important' } ,
39
- maxWidth : { xs : '50px' , md : '240px' } ,
40
- } ,
41
- } }
42
- id = "model-selector"
43
- value = { validModel }
44
- onChange = { ( event : SelectChangeEvent ) => setModel ( event . target . value ) }
40
+ < Tooltip
41
+ title = { t ( 'chat:modelSelectorTooltip' , { hint : KeyCombinations . OPEN_MODEL_SELECTOR ?. hint } ) }
42
+ arrow
43
+ placement = "top"
44
+ disableFocusListener
45
+ disableHoverListener
46
+ onPointerEnter = { ( ) => setTooltipOpen ( true ) }
47
+ onFocus = { ( ) => setTooltipOpen ( true ) }
48
+ onBlur = { ( ) => setTooltipOpen ( false ) }
49
+ onPointerLeave = { ( ) => setTooltipOpen ( false ) }
50
+ ref = { selectRef }
51
+ open = { tooltipOpen }
45
52
>
46
- { availableModels . map ( ( model ) => (
47
- < MenuItem key = { model } value = { model } disabled = { isTokenLimitExceeded && model !== FREE_MODEL } >
48
- < Typography >
49
- { model }
50
- { model === FREE_MODEL && (
51
- < Typography component = "span" sx = { { fontStyle : 'italic' , opacity : 0.8 } } >
52
- { ' ' }
53
- ({ t ( 'chat:freeModel' ) } )
54
- </ Typography >
55
- ) }
56
- { isTokenLimitExceeded && model !== FREE_MODEL && (
57
- < Typography component = "span" sx = { { fontStyle : 'italic' , opacity : 0.8 } } >
58
- { ' ' }
59
- ({ t ( 'chat:modelDisabled' ) } )
60
- </ Typography >
61
- ) }
62
- </ Typography >
63
- </ MenuItem >
64
- ) ) }
65
- </ Select >
53
+ < Select
54
+ ref = { selectRef }
55
+ open = { isOpen }
56
+ onOpen = { ( ) => {
57
+ setIsOpen ( true )
58
+ setTooltipOpen ( true )
59
+ } }
60
+ onClose = { ( event ) => {
61
+ event . stopPropagation ( )
62
+ setTooltipOpen ( false )
63
+ selectRef . current ?. blur ( )
64
+ setIsOpen ( false )
65
+ } }
66
+ sx = { {
67
+ '& .MuiOutlinedInput-notchedOutline' : {
68
+ border : 'none' ,
69
+ } ,
70
+ '& .MuiSelect-select' : {
71
+ overflow : 'hidden' ,
72
+ whiteSpace : 'nowrap' ,
73
+ paddingLeft : { xs : '4px !important' , md : '10px !important' } ,
74
+ paddingRight : { xs : '20px !important' , md : '30px !important' } ,
75
+ maxWidth : { xs : '50px' , md : '240px' } ,
76
+ transition : 'background-color 0.3s ease' ,
77
+ '&:hover' : {
78
+ backgroundColor : 'action.hover' ,
79
+ } ,
80
+ '&:focus' : {
81
+ backgroundColor : 'action.focus' ,
82
+ } ,
83
+ } ,
84
+ } }
85
+ data-testid = "model-selector"
86
+ value = { validModel }
87
+ onChange = { ( event : SelectChangeEvent ) => setModel ( event . target . value ) }
88
+ >
89
+ { availableModels . map ( ( model ) => (
90
+ < MenuItem key = { model } value = { model } disabled = { isTokenLimitExceeded && model !== FREE_MODEL } >
91
+ < Typography >
92
+ { model }
93
+ { model === FREE_MODEL && (
94
+ < Typography component = "span" sx = { { fontStyle : 'italic' , opacity : 0.8 } } >
95
+ { ' ' }
96
+ ({ t ( 'chat:freeModel' ) } )
97
+ </ Typography >
98
+ ) }
99
+ { isTokenLimitExceeded && model !== FREE_MODEL && (
100
+ < Typography component = "span" sx = { { fontStyle : 'italic' , opacity : 0.8 } } >
101
+ { ' ' }
102
+ ({ t ( 'chat:modelDisabled' ) } )
103
+ </ Typography >
104
+ ) }
105
+ </ Typography >
106
+ </ MenuItem >
107
+ ) ) }
108
+ </ Select >
109
+ </ Tooltip >
66
110
</ FormControl >
67
111
)
68
112
}
0 commit comments