All 8 tickets' acceptance criteria checked off (incl. the real OpenClaw E2E verified on t480). Trivial import-sort fix from ruff included.
47 lines
1.2 KiB
TypeScript
47 lines
1.2 KiB
TypeScript
import { create } from 'zustand'
|
|
|
|
export interface TabItem {
|
|
key: string
|
|
title: string
|
|
icon?: React.ReactNode
|
|
closable: boolean
|
|
}
|
|
|
|
interface TabStore {
|
|
tabs: TabItem[]
|
|
activeKey: string
|
|
openTab: (tab: TabItem) => void
|
|
closeTab: (key: string) => void
|
|
setActiveTab: (key: string) => void
|
|
}
|
|
|
|
export const useTabStore = create<TabStore>((set, get) => ({
|
|
tabs: [{ key: '/', title: '仪表盘', closable: false }],
|
|
activeKey: '/',
|
|
|
|
openTab: (tab) => {
|
|
const { tabs, activeKey } = get()
|
|
const exists = tabs.find((t) => t.key === tab.key)
|
|
if (exists) {
|
|
if (activeKey !== tab.key) set({ activeKey: tab.key })
|
|
} else {
|
|
set({ tabs: [...tabs, tab], activeKey: tab.key })
|
|
}
|
|
},
|
|
|
|
closeTab: (key) => {
|
|
const { tabs, activeKey } = get()
|
|
const idx = tabs.findIndex((t) => t.key === key)
|
|
if (idx < 0 || !tabs[idx].closable) return
|
|
|
|
const newTabs = tabs.filter((t) => t.key !== key)
|
|
let newActive = activeKey
|
|
if (activeKey === key) {
|
|
newActive = newTabs[Math.min(idx, newTabs.length - 1)]?.key || '/'
|
|
}
|
|
set({ tabs: newTabs, activeKey: newActive })
|
|
},
|
|
|
|
setActiveTab: (key) => set({ activeKey: key }),
|
|
}))
|