1- import { useTranslation } from 'react-i18next' ;
2- import { useTheme } from '@mui/material/styles' ;
3- import { useFormikContext } from 'formik' ;
4- import { Autocomplete , Box , Chip , FormControl , FormHelperText , Grid , Paper , TextField , Typography } from '@mui/material' ;
1+ import { useTranslation } from 'react-i18next' ;
2+ import { useTheme } from '@mui/material/styles' ;
3+ import { useFormikContext } from 'formik' ;
4+ import { Autocomplete , Box , Chip , FormControl , FormHelperText , Grid , Paper , TextField , Typography } from '@mui/material' ;
55import PropTypes from 'prop-types' ;
6- import { useMemo } from 'react' ;
6+ import { useCallback , useMemo } from 'react' ;
7+
8+ const groupBy = ( option ) => option . owned_by ;
9+ const getOptionLabel = ( option ) => option . name || '' ;
10+ const isOptionEqualToValue = ( option , value ) => option . id === value . id ;
11+ const listboxProps = {
12+ style : {
13+ maxHeight : '400px'
14+ }
15+ } ;
16+
17+ const CustomPaper = ( props ) => (
18+ < Paper
19+ { ...props }
20+ style = { {
21+ boxShadow : '0px 8px 24px rgba(0, 0, 0, 0.15)' ,
22+ borderRadius : '8px'
23+ } }
24+ />
25+ ) ;
726
827const ModelLimitSelector = ( { modelOptions, getModelIcon } ) => {
928 const { t } = useTranslation ( ) ;
1029 const theme = useTheme ( ) ;
1130 const { values, setFieldValue } = useFormikContext ( ) ;
31+
1232 const sortedModelOptions = useMemo ( ( ) => {
1333 return [ ...modelOptions ] . sort ( ( a , b ) => a . owned_by . localeCompare ( b . owned_by ) ) ;
1434 } , [ modelOptions ] ) ;
@@ -18,93 +38,95 @@ const ModelLimitSelector = ({ modelOptions, getModelIcon }) => {
1838 return modelOptions . filter ( ( option ) => selectedIds . has ( option . id ) ) ;
1939 } , [ values . setting ?. limits ?. limit_model_setting ?. models , modelOptions ] ) ;
2040
41+ const handleOnChange = useCallback (
42+ ( event , newValue ) => {
43+ const modelIds = newValue . map ( ( option ) => option . id ) ;
44+ setFieldValue ( 'setting.limits.limit_model_setting.models' , modelIds ) ;
45+ } ,
46+ [ setFieldValue ]
47+ ) ;
48+
49+ const renderOption = useCallback (
50+ ( props , option ) => (
51+ < Box component = "li" { ...props } key = { option . id } sx = { { alignItems : 'flex-start' } } >
52+ < Grid container spacing = { 1 } sx = { { alignItems : 'center' } } >
53+ < Grid item xs = { 'auto' } >
54+ < img
55+ src = { getModelIcon ( option . owned_by ) }
56+ alt = { option . owned_by }
57+ style = { { width : 24 , height : 24 , borderRadius : '4px' } }
58+ onError = { ( e ) => {
59+ e . target . src = '/src/assets/images/icons/unknown_type.svg' ;
60+ } }
61+ />
62+ </ Grid >
63+ < Grid item xs >
64+ < Typography variant = "body1" sx = { { fontWeight : 500 } } >
65+ { option . name }
66+ </ Typography >
67+ < Box sx = { { display : 'flex' , flexWrap : 'wrap' , gap : 0.5 , mt : 0.5 } } >
68+ { option . groups . map ( ( group ) => (
69+ < Chip key = { group } label = { group } size = "small" variant = "outlined" />
70+ ) ) }
71+ </ Box >
72+ </ Grid >
73+ </ Grid >
74+ </ Box >
75+ ) ,
76+ [ getModelIcon ]
77+ ) ;
78+
79+ const renderTags = useCallback (
80+ ( value , getTagProps ) =>
81+ value . map ( ( option , index ) => {
82+ const { key, ...otherTagProps } = getTagProps ( { index } ) ;
83+ return (
84+ < Chip
85+ key = { key }
86+ label = { option . name }
87+ { ...otherTagProps }
88+ size = "small"
89+ variant = "outlined"
90+ color = "primary"
91+ sx = { {
92+ margin : 1 ,
93+ color : theme . palette . primary . main ,
94+ '& .MuiChip-deleteIcon' : {
95+ color : theme . palette . primary . main ,
96+ transition : 'color 0.2s ease-in-out'
97+ } ,
98+ '& .MuiChip-deleteIcon:hover' : {
99+ color : theme . palette . primary . main
100+ }
101+ } }
102+ />
103+ ) ;
104+ } ) ,
105+ [ theme ]
106+ ) ;
107+
21108 return (
22109 < FormControl fullWidth sx = { { ...theme . typography . otherInput } } >
23110 < Autocomplete
24111 multiple
25112 disableListWrap
26113 options = { sortedModelOptions }
27- groupBy = { ( option ) => option . owned_by }
114+ groupBy = { groupBy }
28115 value = { selectedValue }
29- onChange = { ( event , newValue ) => {
30- const modelIds = newValue . map ( ( option ) => option . id ) ;
31- setFieldValue ( 'setting.limits.limit_model_setting.models' , modelIds ) ;
32- } }
33- isOptionEqualToValue = { ( option , value ) => option . id === value . id }
34- getOptionLabel = { ( option ) => option . name || '' }
35- ListboxProps = { {
36- style : {
37- maxHeight : '400px'
38- }
39- } }
116+ onChange = { handleOnChange }
117+ isOptionEqualToValue = { isOptionEqualToValue }
118+ getOptionLabel = { getOptionLabel }
119+ ListboxProps = { listboxProps }
40120 renderInput = { ( params ) => (
41121 < TextField
42122 { ...params }
43123 label = { t ( 'token_index.limit_models' ) }
44124 placeholder = { values . setting ?. limits ?. limit_model_setting ?. models ?. length > 0 ? '' : t ( 'token_index.limit_models_info' ) }
45125 />
46126 ) }
47- renderOption = { ( props , option ) => (
48- < Box component = "li" { ...props } key = { option . id } sx = { { alignItems : 'flex-start' } } >
49- < Grid container spacing = { 1 } sx = { { alignItems : 'center' } } >
50- < Grid item xs = { 'auto' } >
51- < img
52- src = { getModelIcon ( option . owned_by ) }
53- alt = { option . owned_by }
54- style = { { width : 24 , height : 24 , borderRadius : '4px' } }
55- onError = { ( e ) => {
56- e . target . src = '/src/assets/images/icons/unknown_type.svg' ;
57- } }
58- />
59- </ Grid >
60- < Grid item xs >
61- < Typography variant = "body1" sx = { { fontWeight : 500 } } >
62- { option . name }
63- </ Typography >
64- < Box sx = { { display : 'flex' , flexWrap : 'wrap' , gap : 0.5 , mt : 0.5 } } >
65- { option . groups . map ( ( group ) => (
66- < Chip key = { group } label = { group } size = "small" variant = "outlined" />
67- ) ) }
68- </ Box >
69- </ Grid >
70- </ Grid >
71- </ Box >
72- ) }
73- renderTags = { ( value , getTagProps ) =>
74- value . map ( ( option , index ) => {
75- const { key, ...otherTagProps } = getTagProps ( { index } ) ;
76- return (
77- < Chip
78- key = { key }
79- label = { option . name }
80- { ...otherTagProps }
81- size = "small"
82- variant = "outlined"
83- color = "primary"
84- sx = { {
85- margin : 1 ,
86- color : theme . palette . primary . main ,
87- '& .MuiChip-deleteIcon' : {
88- color : theme . palette . primary . main ,
89- transition : 'color 0.2s ease-in-out'
90- } ,
91- '& .MuiChip-deleteIcon:hover' : {
92- color : theme . palette . primary . main
93- }
94- } }
95- />
96- ) ;
97- } )
98- }
99- PaperComponent = { ( props ) => (
100- < Paper
101- { ...props }
102- style = { {
103- boxShadow : '0px 8px 24px rgba(0, 0, 0, 0.15)' ,
104- borderRadius : '8px'
105- } }
106- />
107- ) }
127+ renderOption = { renderOption }
128+ renderTags = { renderTags }
129+ PaperComponent = { CustomPaper }
108130 disableCloseOnSelect
109131 />
110132 < FormHelperText > { t ( 'token_index.limit_models_info' ) } </ FormHelperText >
0 commit comments