feat: multi-tab instrument analysis
Each /instruments/:id navigation opens a persistent tab in the TabBar. Multiple instruments can be open simultaneously — state is preserved when switching. - TabsContext: adds instrumentIds[], openInstrument(), closeInstrument() - TabBar: renders instrument tabs (teal, monospaced, TrendingUp icon) after static tabs, separated by a divider - InstrumentDashboard: accepts instrumentIdProp so keep-alive instances use the right id regardless of URL - App: InstrumentRoute registers the tab on navigation; InstrumentKeepAlive mounts one dashboard per open instrument; NormalRoutes hides on instrument paths (hidden div, Routes still fires) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -1,19 +1,26 @@
|
||||
import { createContext, useContext, useCallback, useState, ReactNode } from 'react'
|
||||
|
||||
interface TabsCtx {
|
||||
kept: ReadonlySet<string>
|
||||
keepRoute: (r: string) => void
|
||||
forgetRoute: (r: string) => void
|
||||
kept: ReadonlySet<string>
|
||||
keepRoute: (r: string) => void
|
||||
forgetRoute: (r: string) => void
|
||||
instrumentIds: string[]
|
||||
openInstrument: (id: string) => void
|
||||
closeInstrument: (id: string) => void
|
||||
}
|
||||
|
||||
const Ctx = createContext<TabsCtx>({
|
||||
kept: new Set(),
|
||||
keepRoute: () => {},
|
||||
forgetRoute: () => {},
|
||||
keepRoute: () => {},
|
||||
forgetRoute: () => {},
|
||||
instrumentIds: [],
|
||||
openInstrument: () => {},
|
||||
closeInstrument: () => {},
|
||||
})
|
||||
|
||||
export function TabsProvider({ children }: { children: ReactNode }) {
|
||||
const [kept, setKept] = useState<Set<string>>(new Set())
|
||||
const [instrumentIds, setInstrumentIds] = useState<string[]>([])
|
||||
|
||||
const keepRoute = useCallback((r: string) =>
|
||||
setKept(p => p.has(r) ? p : new Set([...p, r])), [])
|
||||
@@ -21,7 +28,17 @@ export function TabsProvider({ children }: { children: ReactNode }) {
|
||||
const forgetRoute = useCallback((r: string) =>
|
||||
setKept(p => { const n = new Set(p); n.delete(r); return n }), [])
|
||||
|
||||
return <Ctx.Provider value={{ kept, keepRoute, forgetRoute }}>{children}</Ctx.Provider>
|
||||
const openInstrument = useCallback((id: string) =>
|
||||
setInstrumentIds(p => p.includes(id) ? p : [...p, id]), [])
|
||||
|
||||
const closeInstrument = useCallback((id: string) =>
|
||||
setInstrumentIds(p => p.filter(x => x !== id)), [])
|
||||
|
||||
return (
|
||||
<Ctx.Provider value={{ kept, keepRoute, forgetRoute, instrumentIds, openInstrument, closeInstrument }}>
|
||||
{children}
|
||||
</Ctx.Provider>
|
||||
)
|
||||
}
|
||||
|
||||
export const useTabs = () => useContext(Ctx)
|
||||
|
||||
Reference in New Issue
Block a user