1+ import { Button } from '@mui/material' ;
12import Autocomplete from '@mui/material/Autocomplete' ;
23import CircularProgress from '@mui/material/CircularProgress' ;
34import { debounce } from 'lodash' ;
4- import React , { useCallback , useState } from 'react' ;
5- import { Avatar , Box , Chip , Grid , TextField , Tooltip , Typography } from '../../base' ;
6- import { iconSmall } from '../../constants/iconsSizes' ;
7- import { CloseIcon } from '../../icons/Close' ;
5+ import React , { useEffect , useMemo , useState } from 'react' ;
6+ import { Avatar , Box , Chip , Grid , TextField , Typography } from '../../base' ;
87import { PersonIcon } from '../../icons/Person' ;
98import { useTheme } from '../../theme' ;
10- import { Button } from '@mui/material' ;
11- import { MutationDefinition } from '@reduxjs/toolkit/query' ;
12- import { TypedUseMutationResult } from '@reduxjs/toolkit/dist/query/react' ;
139
1410interface User {
1511 id : string ;
@@ -43,76 +39,96 @@ interface UserSearchFieldProps {
4339 * @returns {Promise<User[]> } A promise that resolves to an array of user suggestions.
4440 */
4541 fetchSuggestions : ( value : string ) => Promise < User [ ] > ;
46- shareWithNewUsers : ( newUsers : User [ ] ) => Promise < { error : string } > ,
42+ shareWithNewUsers : ( newUsers : User [ ] ) => Promise < { error : string } > ;
4743 // isSharing : boolean
4844}
4945
5046const UserShareSearch : React . FC < UserSearchFieldProps > = ( {
5147 usersData,
5248 disabled = false ,
5349 fetchSuggestions,
54- shareWithNewUsers,
50+ shareWithNewUsers
5551} : UserSearchFieldProps ) => {
5652 const [ error , setError ] = useState < string | false > ( false ) ;
57- const [ inputValue , setInputValue ] = useState ( "" )
53+ const [ inputValue , setInputValue ] = useState ( '' ) ;
5854 const [ options , setOptions ] = useState < User [ ] > ( [ ] ) ;
5955 const [ open , setOpen ] = useState ( false ) ;
6056 const [ searchUserLoading , setSearchUserLoading ] = useState ( false ) ;
61- // const [showAllUsers, setShowAllUsers] = useState(false);
57+ const [ usersToShareWith , setUsersToShareWith ] = useState < User [ ] > ( [ ] ) ;
58+ const [ isSharing , setIsSharing ] = useState ( false ) ;
6259 const theme = useTheme ( ) ;
63- const [ usersToShareWith , setUsersToShareWith ] = useState ( [ ] )
64- const [ isSharing , setIsSharing ] = useState ( false )
65-
6660
6761 const handleShareWithNewUsers = async ( ) => {
68-
6962 try {
70- setIsSharing ( true )
71- const result = await shareWithNewUsers ( usersToShareWith )
72- console . log ( " sharing result" , result )
63+ setIsSharing ( true ) ;
64+ const result = await shareWithNewUsers ( usersToShareWith ) ;
65+ console . log ( ' sharing result' , result ) ;
7366 if ( ! result . error ) {
74- setUsersToShareWith ( [ ] )
67+ setUsersToShareWith ( [ ] ) ;
7568 } else {
76- setError ( result . error )
69+ setError ( result . error ) ;
7770 }
78- }
79- catch ( e ) {
80- console . log ( "error while sharing" , e )
71+ } catch ( e ) {
72+ console . log ( 'error while sharing' , e ) ;
8173 } finally {
82- setIsSharing ( false )
74+ setIsSharing ( false ) ;
8375 }
84- }
76+ } ;
8577
8678 const handleAdd = ( _event : React . SyntheticEvent < Element , Event > , value : User [ ] ) => {
8779 if ( value ) {
88- console . log ( " add value" , value )
89- setUsersToShareWith ( value )
90- setInputValue ( "" )
91- setOpen ( false )
80+ console . log ( ' add value' , value ) ;
81+ setUsersToShareWith ( value ) ;
82+ setInputValue ( '' ) ;
83+ setOpen ( false ) ;
9284 }
9385 } ;
9486
95- const handleInputChange = useCallback ( debounce (
96- async ( value : string ) => {
97- console . log ( "inputChange" , value )
98- if ( value === '' ) {
99- setOptions ( [ ] ) ;
100- setOpen ( false ) ;
101- } else {
102- setSearchUserLoading ( true ) ;
103- const suggestions = await fetchSuggestions ( value ) ;
104- setOptions ( suggestions ) ;
105- setSearchUserLoading ( false ) ;
106- setError ( false ) ;
107- setOpen ( true ) ;
108- }
109- } ,
110- 300
111- ) , [ fetchSuggestions ] ) ;
87+ // Memoize the debounced function to prevent recreation on each render
88+ const debouncedFetchSuggestions = useMemo (
89+ ( ) =>
90+ debounce ( async ( value : string ) => {
91+ console . log ( 'debounced fetch running for:' , value ) ;
92+ if ( value === '' ) {
93+ setOptions ( [ ] ) ;
94+ setOpen ( false ) ;
95+ } else {
96+ setSearchUserLoading ( true ) ;
97+ const suggestions = await fetchSuggestions ( value ) ;
98+ setOptions ( suggestions ) ;
99+ setSearchUserLoading ( false ) ;
100+ setError ( false ) ;
101+ setOpen ( true ) ;
102+ }
103+ } , 300 ) ,
104+ [ fetchSuggestions ]
105+ ) ;
112106
113- const filteredOptions = options . filter ( option => ! usersToShareWith . concat ( usersData ) . find ( u => u . id == option . id ) )
107+ // Clean up debounce on unmount
108+ useEffect ( ( ) => {
109+ return ( ) => {
110+ debouncedFetchSuggestions . cancel ( ) ;
111+ } ;
112+ } , [ debouncedFetchSuggestions ] ) ;
114113
115- const isShareDisabled = disabled || isSharing || usersToShareWith . length == 0
114+ // Handler for input changes
115+ const handleInputChange = ( event : React . SyntheticEvent , value : string , reason : string ) => {
116+ // Only process actual typing events, not clearing or blurring
117+ if ( reason === 'input' ) {
118+ console . log ( 'input change:' , value ) ;
119+ setInputValue ( value ) ;
120+ debouncedFetchSuggestions ( value ) ;
121+ } else if ( reason === 'clear' ) {
122+ setInputValue ( '' ) ;
123+ setOptions ( [ ] ) ;
124+ }
125+ } ;
126+
127+ const filteredOptions = options . filter (
128+ ( option ) => ! usersToShareWith . concat ( usersData ) . find ( ( u ) => u . id === option . id )
129+ ) ;
130+
131+ const isShareDisabled = disabled || isSharing || usersToShareWith . length === 0 ;
116132
117133 const UserChip = ( { avatarObj, ...props } : { avatarObj : User } ) => (
118134 < Chip
@@ -130,15 +146,17 @@ const UserShareSearch: React.FC<UserSearchFieldProps> = ({
130146
131147 return (
132148 < >
133- < Box display = { "flex" } alignItems = { "center" } justifyContent = { "space-between" } gap = { 1 } >
149+ < Box display = "flex" alignItems = "center" justifyContent = "space-between" gap = { 1 } >
134150 < Autocomplete
135151 id = "user-search-field"
136152 sx = { { width : '100%' } }
137153 // eslint-disable-next-line @typescript-eslint/ban-ts-comment
138154 // @ts -ignore
139155 filterOptions = { ( x ) => x }
140156 options = { filteredOptions }
141- renderTags = { ( value , getTagProps ) => value . map ( ( user , index ) => < UserChip avatarObj = { user } { ...getTagProps ( { index } ) } /> ) }
157+ renderTags = { ( value , getTagProps ) =>
158+ value . map ( ( user , index ) => < UserChip avatarObj = { user } { ...getTagProps ( { index } ) } /> )
159+ }
142160 disableClearable
143161 includeInputInList
144162 filterSelectedOptions
@@ -152,35 +170,25 @@ const UserShareSearch: React.FC<UserSearchFieldProps> = ({
152170 getOptionLabel = { ( user ) => user . email }
153171 noOptionsText = { searchUserLoading ? 'Loading...' : 'No users found' }
154172 onChange = { handleAdd }
155- onInputChange = { ( event , value ) => {
156- console . log ( "onChange" , value )
157- setInputValue ( value )
158- handleInputChange ( value )
159- } }
160-
173+ onInputChange = { handleInputChange }
161174 isOptionEqualToValue = { ( option , value ) => option . id === value . id }
162- // clearOnBlur
163-
164175 renderInput = { ( params ) => (
165176 < TextField
166177 { ...params }
167- // label={label || 'Add User'}
168- placeholder = 'Add Users'
178+ placeholder = "Add Users"
169179 error = { ! ! error }
170180 helperText = { error }
171181 fullWidth
172182 label = ""
173183 disabled = { isShareDisabled }
174-
175184 sx = { {
176- " & .MuiOutlinedInput-root" : {
177- paddingInline : " 0.5rem" ,
178- paddingBlock : " 0.1rem"
185+ ' & .MuiOutlinedInput-root' : {
186+ paddingInline : ' 0.5rem' ,
187+ paddingBlock : ' 0.1rem'
179188 }
180189 } }
181190 InputProps = { {
182191 ...params . InputProps ,
183-
184192 endAdornment : (
185193 < > { searchUserLoading ? < CircularProgress color = "inherit" size = { 20 } /> : null } </ >
186194 )
@@ -219,14 +227,27 @@ const UserShareSearch: React.FC<UserSearchFieldProps> = ({
219227 ) }
220228 />
221229
222- < Button variant = 'contained' color = 'primary'
230+ < Button
231+ variant = "contained"
223232 sx = { {
224- backgroundColor : isShareDisabled ? theme . palette . action . disabled : theme . palette . action . active
233+ '&.Mui-disabled' : {
234+ color : `${ theme . palette . text . secondary } !important` ,
235+ backgroundColor : `${ theme . palette . action . disabled } !important` // This ensures the color stays when disabled
236+ }
225237 } }
226- onClick = { handleShareWithNewUsers } disabled = { isShareDisabled } >
227- { isSharing ? < CircularProgress size = { 24 } sx = { {
228- color : "#fff"
229- } } /> : "Share" }
238+ onClick = { handleShareWithNewUsers }
239+ disabled = { isShareDisabled }
240+ >
241+ { isSharing ? (
242+ < CircularProgress
243+ size = { 24 }
244+ sx = { {
245+ color : '#fff'
246+ } }
247+ />
248+ ) : (
249+ 'Share'
250+ ) }
230251 </ Button >
231252 </ Box >
232253 </ >
0 commit comments