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