github.com/tilt-dev/tilt@v0.36.0/web/src/ResourceGroupsContext.tsx (about) 1 import { createContext, PropsWithChildren, useContext, useMemo } from "react" 2 import { usePersistentState } from "./BrowserStorage" 3 4 export type GroupState = { expanded: boolean } 5 6 export type GroupsState = { 7 [key: string]: GroupState 8 } 9 10 type ResourceGroupsContext = { 11 groups: GroupsState 12 getGroup: (groupLabel: string) => GroupState 13 toggleGroupExpanded: (groupLabel: string) => void 14 expandAll: () => void 15 collapseAll: (groups: string[]) => void 16 } 17 18 export const DEFAULT_EXPANDED_STATE = true 19 export const DEFAULT_GROUP_STATE: GroupState = { 20 expanded: DEFAULT_EXPANDED_STATE, 21 } 22 23 const resourceGroupsContext = createContext<ResourceGroupsContext>({ 24 groups: {}, 25 toggleGroupExpanded: () => { 26 console.warn("Resource group context is not set.") 27 }, 28 getGroup: () => { 29 console.warn("Resource group context is not set.") 30 return { ...DEFAULT_GROUP_STATE } 31 }, 32 expandAll: () => void 0, 33 collapseAll: (groups: string[]) => void 0, 34 }) 35 36 export function useResourceGroups(): ResourceGroupsContext { 37 return useContext(resourceGroupsContext) 38 } 39 40 export function ResourceGroupsContextProvider( 41 props: PropsWithChildren<{ initialValuesForTesting?: GroupsState }> 42 ) { 43 const defaultPersistentValue = props.initialValuesForTesting ?? {} 44 const [groups, setGroups] = usePersistentState<GroupsState>( 45 "resource-groups", 46 defaultPersistentValue 47 ) 48 49 const value: ResourceGroupsContext = useMemo(() => { 50 function toggleGroupExpanded(groupLabel: string) { 51 const currentGroupState = groups[groupLabel] ?? { ...DEFAULT_GROUP_STATE } 52 const nextGroupState = { 53 ...currentGroupState, 54 expanded: !currentGroupState.expanded, 55 } 56 57 setGroups((prevState) => { 58 return { 59 ...prevState, 60 [groupLabel]: nextGroupState, 61 } 62 }) 63 } 64 65 function getGroup(groupLabel: string) { 66 return groups[groupLabel] ?? { ...DEFAULT_GROUP_STATE } 67 } 68 69 // We expand all groups by resetting the collapse state to empty. 70 // 71 // This ensures that even hidden groups are expanded. 72 // 73 // NOTE(nick): expandAll and collapseAll are non-symmetric - they have 74 // very different behavior for groups that are currently hidden, or 75 // for new groups created after the button is clicked. We deliberately 76 // err on the side of expanding. 77 function expandAll() { 78 setGroups({}) // Reset state. 79 } 80 81 // We can collapse all groups currently on-screen. 82 // 83 // If new resources with new names come later, we'll leave them expanded. 84 function collapseAll(groupNames: string[]) { 85 let newState: GroupsState = {} 86 groupNames.forEach((group) => (newState[group] = { expanded: false })) 87 setGroups(newState) 88 } 89 90 return { 91 groups, 92 toggleGroupExpanded, 93 getGroup, 94 expandAll, 95 collapseAll, 96 } 97 }, [groups, setGroups]) 98 99 return ( 100 <resourceGroupsContext.Provider value={value}> 101 {props.children} 102 </resourceGroupsContext.Provider> 103 ) 104 }