github.com/tilt-dev/tilt@v0.36.0/web/src/OverviewResourcePane.tsx (about)

     1  import React, { useEffect, useState } from "react"
     2  import SplitPane from "react-split-pane"
     3  import styled from "styled-components"
     4  import { Alert, combinedAlerts } from "./alerts"
     5  import { ApiButtonType, buttonsForComponent } from "./ApiButton"
     6  import HeaderBar, { HeaderBarPage } from "./HeaderBar"
     7  import { LogUpdateAction, LogUpdateEvent, useLogStore } from "./LogStore"
     8  import OverviewResourceDetails from "./OverviewResourceDetails"
     9  import OverviewResourceSidebar from "./OverviewResourceSidebar"
    10  import "./Resizer.scss"
    11  import { useResourceNav } from "./ResourceNav"
    12  import { useSidebarContext } from "./SidebarContext"
    13  import StarredResourceBar, {
    14    starredResourcePropsFromView,
    15  } from "./StarredResourceBar"
    16  import { Color, Width } from "./style-helpers"
    17  import { ResourceName, UIResource } from "./types"
    18  
    19  type OverviewResourcePaneProps = {
    20    view: Proto.webviewView
    21    isSocketConnected: boolean
    22  }
    23  
    24  let OverviewResourcePaneRoot = styled.div`
    25    display: flex;
    26    flex-direction: column;
    27    width: 100%;
    28    height: 100vh;
    29    background-color: ${Color.gray20};
    30    max-height: 100%;
    31  `
    32  let Main = styled.div`
    33    display: flex;
    34    width: 100%;
    35    // In Safari, flex-basis "auto" squishes OverviewTabBar + OverviewResourceBar
    36    flex: 1 1 100%;
    37    overflow: hidden;
    38    position: relative;
    39  
    40    .SplitPane {
    41      position: relative !important;
    42    }
    43    .Pane {
    44      display: flex;
    45    }
    46  `
    47  
    48  export default function OverviewResourcePane(props: OverviewResourcePaneProps) {
    49    let nav = useResourceNav()
    50    const logStore = useLogStore()
    51    let resources = props.view?.uiResources || []
    52    let name = nav.invalidResource || nav.selectedResource || ""
    53    let r: UIResource | undefined
    54    let all = name === "" || name === ResourceName.all
    55    let starred = name === ResourceName.starred
    56    if (!all) {
    57      r = resources.find((r) => r.metadata?.name === name)
    58    }
    59    let selectedTab = ""
    60    if (all) {
    61      selectedTab = ResourceName.all
    62    } else if (starred) {
    63      selectedTab = ResourceName.starred
    64    } else if (r?.metadata?.name) {
    65      selectedTab = r.metadata.name
    66    }
    67  
    68    const { isSidebarOpen, setSidebarOpen, setSidebarClosed } =
    69      useSidebarContext()
    70  
    71    const [paneSize, setPaneSize] = useState<number>(
    72      isSidebarOpen ? Width.sidebarDefault : Width.sidebarMinimum
    73    )
    74  
    75    // listen for changes from sidebar context in case it was toggled instead
    76    // being dragged past a breakpoint.
    77    useEffect(() => {
    78      setPaneSize(
    79        isSidebarOpen ? Width.sidebarDefault : Width.sidebarMinimum + 0.01
    80        // adds 0.01 so there's still a state diff when the user releases after dragging
    81      )
    82    }, [isSidebarOpen])
    83  
    84    const [truncateCount, setTruncateCount] = useState<number>(0)
    85  
    86    // add a listener to rebuild alerts whenever a truncation event occurs
    87    // truncateCount is a dummy state variable to trigger a re-render to
    88    // simplify logic vs reconciliation between logStore + props
    89    useEffect(() => {
    90      const rebuildAlertsOnLogClear = (e: LogUpdateEvent) => {
    91        if (e.action === LogUpdateAction.truncate) {
    92          setTruncateCount(truncateCount + 1)
    93        }
    94      }
    95  
    96      logStore.addUpdateListener(rebuildAlertsOnLogClear)
    97      return () => logStore.removeUpdateListener(rebuildAlertsOnLogClear)
    98    }, [truncateCount])
    99  
   100    let alerts: Alert[] = []
   101    if (r) {
   102      alerts = combinedAlerts(r, logStore)
   103    } else if (all) {
   104      resources.forEach((r) => alerts.push(...combinedAlerts(r, logStore)))
   105    }
   106  
   107    const buttons = buttonsForComponent(
   108      props.view.uiButtons,
   109      ApiButtonType.Resource,
   110      name
   111    )
   112  
   113    const handleSplitPaneResize = (newSize: number) => {
   114      if (newSize < Width.sidebarBreakpoint && isSidebarOpen) {
   115        setSidebarClosed()
   116      } else if (newSize >= Width.sidebarBreakpoint && !isSidebarOpen) {
   117        setSidebarOpen()
   118      }
   119    }
   120  
   121    return (
   122      <OverviewResourcePaneRoot>
   123        <HeaderBar
   124          view={props.view}
   125          currentPage={HeaderBarPage.Detail}
   126          isSocketConnected={props.isSocketConnected}
   127        />
   128        <StarredResourceBar
   129          {...starredResourcePropsFromView(props.view, selectedTab)}
   130        />
   131        <Main>
   132          <SplitPane
   133            split="vertical"
   134            size={paneSize}
   135            minSize={Width.sidebarMinimum}
   136            onChange={handleSplitPaneResize}
   137            onDragFinished={() =>
   138              setPaneSize(
   139                isSidebarOpen ? Width.sidebarDefault : Width.sidebarMinimum
   140              )
   141            }
   142          >
   143            <OverviewResourceSidebar {...props} name={name} />
   144            <OverviewResourceDetails
   145              resource={r}
   146              name={name}
   147              alerts={alerts}
   148              buttons={buttons}
   149            />
   150          </SplitPane>
   151        </Main>
   152      </OverviewResourcePaneRoot>
   153    )
   154  }