github.com/tilt-dev/tilt@v0.33.15-0.20240515162809-0a22ed45d8a0/web/src/ResourceGroupsContext.tsx (about)

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