55 createFilterOptions ,
66 TextField ,
77 IconButton ,
8+ Tooltip ,
89} from "@mui/material" ;
910import { useEffect , useState , useMemo , useCallback , useRef } from "react" ;
1011import { useSettings } from "../../hooks/use-settings" ;
@@ -89,6 +90,7 @@ export const CippAutoComplete = (props) => {
8990 const [ offCanvasVisible , setOffCanvasVisible ] = useState ( false ) ;
9091 const [ fullObject , setFullObject ] = useState ( null ) ;
9192 const [ internalValue , setInternalValue ] = useState ( null ) ; // Track selected value internally
93+ const [ open , setOpen ] = useState ( false ) ; // Control popover open state
9294
9395 // Sync internalValue when external value or defaultValue prop changes (e.g., when editing a form)
9496 useEffect ( ( ) => {
@@ -295,6 +297,15 @@ export const CippAutoComplete = (props) => {
295297 < Autocomplete
296298 ref = { autocompleteRef }
297299 key = { stableKey }
300+ open = { open }
301+ onOpen = { ( ) => setOpen ( true ) }
302+ onClose = { ( event , reason ) => {
303+ // Keep open if Tab was used in multiple mode
304+ if ( reason === "selectOption" && multiple && event ?. type === "click" ) {
305+ return ;
306+ }
307+ setOpen ( false ) ;
308+ } }
298309 disabled = { disabled || actionGetRequest . isFetching || isFetching }
299310 popupIcon = {
300311 actionGetRequest . isFetching || isFetching ? (
@@ -425,6 +436,17 @@ export const CippAutoComplete = (props) => {
425436 event . preventDefault ( ) ;
426437 // Trigger a click on the highlighted option
427438 highlightedOption . click ( ) ;
439+
440+ // In multiple mode, keep the popover open and refocus
441+ if ( multiple ) {
442+ setTimeout ( ( ) => {
443+ setOpen ( true ) ;
444+ const input = autocompleteRef . current ?. querySelector ( "input" ) ;
445+ if ( input ) {
446+ input . focus ( ) ;
447+ }
448+ } , 50 ) ;
449+ }
428450 }
429451 }
430452 } }
@@ -439,79 +461,83 @@ export const CippAutoComplete = (props) => {
439461 { ...other }
440462 />
441463 { api ?. url && api ?. showRefresh && (
442- < IconButton
443- size = "small"
444- onClick = { ( ) => {
445- actionGetRequest . refetch ( ) ;
446- } }
447- >
448- < Sync />
449- </ IconButton >
464+ < Tooltip title = "Refresh" >
465+ < IconButton
466+ size = "small"
467+ onClick = { ( ) => {
468+ actionGetRequest . refetch ( ) ;
469+ } }
470+ >
471+ < Sync />
472+ </ IconButton >
473+ </ Tooltip >
450474 ) }
451475 { api ?. templateView && (
452- < IconButton
453- size = "small"
454- onClick = { ( ) => {
455- // Use internalValue if value prop is not available
456- const currentValue = value || internalValue ;
457-
458- // Get the full object from the selected value
459- if ( multiple ) {
460- // For multiple selection, get all full objects
461- const fullObjects = currentValue
462- . map ( ( v ) => {
463- const valueToFind = v ?. value || v ;
464- const found = usedOptions . find ( ( opt ) => opt . value === valueToFind ) ;
465- let rawData = found ?. rawData ;
466-
467- // If property is specified, extract and parse JSON from that property
468- if ( rawData && api ?. templateView ?. property ) {
469- try {
470- const propertyValue = rawData [ api . templateView . property ] ;
471- if ( typeof propertyValue === "string" ) {
472- rawData = JSON . parse ( propertyValue ) ;
473- } else {
474- rawData = propertyValue ;
476+ < Tooltip title = { `View ${ api ?. templateView . title } ` || "View details" } >
477+ < IconButton
478+ size = "small"
479+ onClick = { ( ) => {
480+ // Use internalValue if value prop is not available
481+ const currentValue = value || internalValue ;
482+
483+ // Get the full object from the selected value
484+ if ( multiple ) {
485+ // For multiple selection, get all full objects
486+ const fullObjects = currentValue
487+ . map ( ( v ) => {
488+ const valueToFind = v ?. value || v ;
489+ const found = usedOptions . find ( ( opt ) => opt . value === valueToFind ) ;
490+ let rawData = found ?. rawData ;
491+
492+ // If property is specified, extract and parse JSON from that property
493+ if ( rawData && api ?. templateView ?. property ) {
494+ try {
495+ const propertyValue = rawData [ api . templateView . property ] ;
496+ if ( typeof propertyValue === "string" ) {
497+ rawData = JSON . parse ( propertyValue ) ;
498+ } else {
499+ rawData = propertyValue ;
500+ }
501+ } catch ( e ) {
502+ console . error ( "Failed to parse JSON from property:" , e ) ;
503+ // Keep original rawData if parsing fails
475504 }
476- } catch ( e ) {
477- console . error ( "Failed to parse JSON from property:" , e ) ;
478- // Keep original rawData if parsing fails
479505 }
480- }
481506
482- return rawData ;
483- } )
484- . filter ( Boolean ) ;
485- setFullObject ( fullObjects ) ;
486- } else {
487- // For single selection, get the full object
488- const valueToFind = currentValue ?. value || currentValue ;
489- const selectedOption = usedOptions . find ( ( opt ) => opt . value === valueToFind ) ;
490- let rawData = selectedOption ?. rawData || null ;
491-
492- // If property is specified, extract and parse JSON from that property
493- if ( rawData && api ?. templateView ?. property ) {
494- try {
495- const propertyValue = rawData [ api . templateView . property ] ;
496- if ( typeof propertyValue === "string" ) {
497- rawData = JSON . parse ( propertyValue ) ;
498- } else {
499- rawData = propertyValue ;
507+ return rawData ;
508+ } )
509+ . filter ( Boolean ) ;
510+ setFullObject ( fullObjects ) ;
511+ } else {
512+ // For single selection, get the full object
513+ const valueToFind = currentValue ?. value || currentValue ;
514+ const selectedOption = usedOptions . find ( ( opt ) => opt . value === valueToFind ) ;
515+ let rawData = selectedOption ?. rawData || null ;
516+
517+ // If property is specified, extract and parse JSON from that property
518+ if ( rawData && api ?. templateView ?. property ) {
519+ try {
520+ const propertyValue = rawData [ api . templateView . property ] ;
521+ if ( typeof propertyValue === "string" ) {
522+ rawData = JSON . parse ( propertyValue ) ;
523+ } else {
524+ rawData = propertyValue ;
525+ }
526+ } catch ( e ) {
527+ console . error ( "Failed to parse JSON from property:" , e ) ;
528+ // Keep original rawData if parsing fails
500529 }
501- } catch ( e ) {
502- console . error ( "Failed to parse JSON from property:" , e ) ;
503- // Keep original rawData if parsing fails
504530 }
505- }
506531
507- setFullObject ( rawData ) ;
508- }
509- setOffCanvasVisible ( true ) ;
510- } }
511- title = { api ?. templateView . title || "View details" }
512- >
513- < Visibility />
514- </ IconButton >
532+ setFullObject ( rawData ) ;
533+ }
534+ setOffCanvasVisible ( true ) ;
535+ } }
536+ title = { api ?. templateView . title || "View details" }
537+ >
538+ < Visibility />
539+ </ IconButton >
540+ </ Tooltip >
515541 ) }
516542 </ Stack >
517543 ) }
0 commit comments