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  }