github.com/turbot/steampipe@v1.7.0-rc.0.0.20240517123944-7cef272d4458/ui/dashboard/src/hooks/useContainer.tsx (about) 1 import { 2 createContext, 3 ReactNode, 4 useCallback, 5 useContext, 6 useEffect, 7 useState, 8 } from "react"; 9 import { noop } from "../utils/func"; 10 import { PanelDefinition } from "../types"; 11 12 export type ContainerChildVisibility = "visible" | "hidden"; 13 14 type ContainerChildrenVisibility = { 15 [key in ContainerChildVisibility]: PanelDefinition[]; 16 }; 17 18 type IContainerContext = { 19 childVisibility?: ContainerChildrenVisibility; 20 showTitle: boolean; 21 updateChildStatus: ( 22 panel: PanelDefinition, 23 visibility: ContainerChildVisibility 24 ) => void; 25 }; 26 27 type ContainerProviderProps = { 28 children: ReactNode; 29 }; 30 31 const ContainerContext = createContext<IContainerContext | null>({ 32 showTitle: true, 33 updateChildStatus: noop, 34 }); 35 36 const ContainerProvider = ({ children }: ContainerProviderProps) => { 37 const [childVisibility, setChildVisibility] = 38 useState<ContainerChildrenVisibility>({ 39 visible: [], 40 hidden: [], 41 }); 42 const [showTitle, setShowTitle] = useState(false); 43 44 const updateChildStatus = useCallback( 45 (child: PanelDefinition, visibility: ContainerChildVisibility) => { 46 // Determine if the child is already marked as visible or hidden and update 47 // state if required to force a re-render of the container's title 48 49 const visibleIndex = childVisibility.visible.findIndex( 50 (p) => p.name === child.name 51 ); 52 const hiddenIndex = childVisibility.hidden.findIndex( 53 (p) => p.name === child.name 54 ); 55 if (visibility === "visible") { 56 // If it's already marked as visible, nothing to do 57 if (visibleIndex > -1 && hiddenIndex === -1) { 58 return; 59 } 60 // Add to visible list if required 61 const newVisible = 62 visibleIndex > -1 63 ? childVisibility.visible 64 : [...childVisibility.visible, child]; 65 // Remove from hidden list if required 66 const newHidden = 67 hiddenIndex > -1 68 ? [ 69 ...childVisibility.hidden.slice(0, hiddenIndex), 70 ...childVisibility.hidden.slice( 71 hiddenIndex + 1, 72 childVisibility.hidden.length - 1 73 ), 74 ] 75 : childVisibility.hidden; 76 setChildVisibility({ 77 visible: newVisible, 78 hidden: newHidden, 79 }); 80 } else if (visibility === "hidden") { 81 // If it's already marked as hidden, nothing to do 82 if (hiddenIndex > -1 && visibleIndex === -1) { 83 return; 84 } 85 // Remove from visible list if required 86 const newVisible = 87 visibleIndex > -1 88 ? [ 89 ...childVisibility.visible.slice(0, visibleIndex), 90 ...childVisibility.visible.slice( 91 visibleIndex + 1, 92 childVisibility.visible.length - 1 93 ), 94 ] 95 : childVisibility.visible; 96 // Add to hidden list if required 97 const newHidden = 98 hiddenIndex > -1 99 ? childVisibility.hidden 100 : [...childVisibility.hidden, child]; 101 setChildVisibility({ 102 visible: newVisible, 103 hidden: newHidden, 104 }); 105 } 106 }, 107 [childVisibility, setChildVisibility] 108 ); 109 110 useEffect(() => { 111 if ( 112 showTitle && 113 childVisibility.hidden.length > 0 && 114 childVisibility.visible.length === 0 115 ) { 116 setShowTitle(false); 117 } else if (!showTitle && childVisibility.visible.length > 0) { 118 setShowTitle(true); 119 } 120 }, [childVisibility, showTitle, setShowTitle]); 121 122 return ( 123 <ContainerContext.Provider 124 value={{ 125 childVisibility, 126 showTitle, 127 updateChildStatus, 128 }} 129 > 130 {children} 131 </ContainerContext.Provider> 132 ); 133 }; 134 135 const useContainer = () => { 136 const context = useContext(ContainerContext); 137 if (context === undefined) { 138 // Normally we'd throw an error here, but we may not be in the context of 139 // a container, so I'll just send some sensible defaults 140 return { 141 showTitle: true, 142 updateChildStatus: noop, 143 }; 144 } 145 return context as IContainerContext; 146 }; 147 148 export { ContainerContext, ContainerProvider, useContainer };