mirror of
https://github.com/Azure/cosmos-explorer.git
synced 2026-09-22 10:32:23 +01:00
f8cfb33618
* Remove Phoenix and notebook core Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 4e4f2ebd-f2f8-4c2b-b63d-40e1a2b1c662 * Fix merge issue. --------- Copilot-Session: 4e4f2ebd-f2f8-4c2b-b63d-40e1a2b1c662
214 lines
7.4 KiB
TypeScript
214 lines
7.4 KiB
TypeScript
import { clamp } from "@fluentui/react";
|
|
import { OpenTab } from "Contracts/ActionContracts";
|
|
import { useSelectedNode } from "Explorer/useSelectedNode";
|
|
import { isFabricMirrored } from "Platform/Fabric/FabricUtil";
|
|
import {
|
|
AppStateComponentNames,
|
|
OPEN_TABS_SUBCOMPONENT_NAME,
|
|
saveSubComponentState,
|
|
} from "Shared/AppStatePersistenceUtility";
|
|
import create, { UseStore } from "zustand";
|
|
import * as ViewModels from "../Contracts/ViewModels";
|
|
import TabsBase from "../Explorer/Tabs/TabsBase";
|
|
|
|
export interface TabsState {
|
|
openedTabs: TabsBase[];
|
|
openedReactTabs: ReactTabKind[];
|
|
activeTab: TabsBase | undefined;
|
|
activeReactTab: ReactTabKind | undefined;
|
|
isTabExecuting: boolean;
|
|
isQueryErrorThrown: boolean;
|
|
activateTab: (tab: TabsBase) => void;
|
|
activateNewTab: (tab: TabsBase) => void;
|
|
activateReactTab: (tabkind: ReactTabKind) => void;
|
|
updateTab: (tab: TabsBase) => void;
|
|
getTabs: (tabKind: ViewModels.CollectionTabKind, comparator?: (tab: TabsBase) => boolean) => TabsBase[];
|
|
refreshActiveTab: (comparator: (tab: TabsBase) => boolean) => void;
|
|
closeTabsByComparator: (comparator: (tab: TabsBase) => boolean) => void;
|
|
closeTab: (tab: TabsBase) => void;
|
|
openAndActivateReactTab: (tabKind: ReactTabKind) => void;
|
|
closeReactTab: (tabKind: ReactTabKind) => void;
|
|
setIsTabExecuting: (state: boolean) => void;
|
|
setIsQueryErrorThrown: (state: boolean) => void;
|
|
getCurrentTabIndex: () => number;
|
|
selectTabByIndex: (index: number) => void;
|
|
selectLeftTab: () => void;
|
|
selectRightTab: () => void;
|
|
closeActiveTab: () => void;
|
|
closeAllTabs: () => void;
|
|
persistTabsState: () => void;
|
|
}
|
|
|
|
export enum ReactTabKind {
|
|
Connect,
|
|
Home,
|
|
Quickstart,
|
|
}
|
|
|
|
export const useTabs: UseStore<TabsState> = create((set, get) => ({
|
|
openedTabs: [] as TabsBase[],
|
|
openedReactTabs: [ReactTabKind.Home],
|
|
activeTab: undefined as TabsBase,
|
|
activeReactTab: ReactTabKind.Home,
|
|
isTabExecuting: false,
|
|
isQueryErrorThrown: false,
|
|
activateTab: (tab: TabsBase): void => {
|
|
if (get().openedTabs.some((openedTab) => openedTab.tabId === tab.tabId)) {
|
|
set({ activeTab: tab, activeReactTab: undefined });
|
|
tab.onActivate();
|
|
}
|
|
},
|
|
activateNewTab: (tab: TabsBase): void => {
|
|
set((state) => ({
|
|
openedTabs: [...state.openedTabs, tab],
|
|
activeTab: tab,
|
|
activeReactTab: undefined as ReactTabKind | undefined,
|
|
}));
|
|
tab.triggerPersistState = get().persistTabsState;
|
|
tab.onActivate();
|
|
get().persistTabsState();
|
|
},
|
|
activateReactTab: (tabKind: ReactTabKind): void => {
|
|
// Clear the selected node when switching to a react tab.
|
|
useSelectedNode.getState().setSelectedNode(undefined);
|
|
set({ activeTab: undefined, activeReactTab: tabKind });
|
|
},
|
|
updateTab: (tab: TabsBase) => {
|
|
if (get().activeTab?.tabId === tab.tabId) {
|
|
set({ activeTab: tab });
|
|
}
|
|
|
|
set((state) => ({
|
|
openedTabs: state.openedTabs.map((openedTab) => {
|
|
if (openedTab.tabId === tab.tabId) {
|
|
return tab;
|
|
}
|
|
return openedTab;
|
|
}),
|
|
}));
|
|
},
|
|
getTabs: (tabKind: ViewModels.CollectionTabKind, comparator?: (tab: TabsBase) => boolean): TabsBase[] =>
|
|
get().openedTabs.filter((tab) => tab.tabKind === tabKind && (!comparator || comparator(tab))),
|
|
refreshActiveTab: (comparator: (tab: TabsBase) => boolean): void => {
|
|
// ensures that the tab selects/highlights the right node based on resource tree expand/collapse state
|
|
const activeTab = get().activeTab;
|
|
activeTab && comparator(activeTab) && activeTab.onActivate();
|
|
},
|
|
closeTabsByComparator: (comparator: (tab: TabsBase) => boolean): void =>
|
|
get()
|
|
.openedTabs.filter(comparator)
|
|
.forEach((tab) => tab.onCloseTabButtonClick()),
|
|
closeTab: (tab: TabsBase): void => {
|
|
let tabIndex: number;
|
|
const { activeTab, openedTabs, openedReactTabs } = get();
|
|
const updatedTabs = openedTabs.filter((openedTab, index) => {
|
|
if (tab.tabId === openedTab.tabId) {
|
|
tabIndex = index;
|
|
return false;
|
|
}
|
|
return true;
|
|
});
|
|
if (updatedTabs.length === 0 && !isFabricMirrored()) {
|
|
set({ activeTab: undefined, activeReactTab: undefined });
|
|
}
|
|
|
|
if (activeTab && tab.tabId === activeTab.tabId && tabIndex !== -1) {
|
|
const tabToTheRight = updatedTabs[tabIndex];
|
|
const lastOpenTab = updatedTabs[updatedTabs.length - 1];
|
|
const newActiveTab = tabToTheRight ?? lastOpenTab;
|
|
set({ activeTab: newActiveTab });
|
|
if (newActiveTab) {
|
|
newActiveTab.onActivate();
|
|
}
|
|
}
|
|
|
|
set({ openedTabs: updatedTabs });
|
|
|
|
if (updatedTabs.length === 0 && openedReactTabs.length > 0) {
|
|
set({ activeTab: undefined, activeReactTab: openedReactTabs[openedReactTabs.length - 1] });
|
|
}
|
|
|
|
get().persistTabsState();
|
|
},
|
|
openAndActivateReactTab: (tabKind: ReactTabKind) => {
|
|
if (get().openedReactTabs.indexOf(tabKind) === -1) {
|
|
set((state) => ({
|
|
openedReactTabs: [...state.openedReactTabs, tabKind],
|
|
}));
|
|
}
|
|
|
|
set({ activeTab: undefined, activeReactTab: tabKind });
|
|
},
|
|
closeReactTab: (tabKind: ReactTabKind) => {
|
|
const { activeReactTab, openedTabs, openedReactTabs } = get();
|
|
const updatedOpenedReactTabs = openedReactTabs.filter((tab: ReactTabKind) => tabKind !== tab);
|
|
if (activeReactTab === tabKind) {
|
|
openedTabs?.length > 0
|
|
? set({ activeTab: openedTabs[0], activeReactTab: undefined })
|
|
: set({ activeTab: undefined, activeReactTab: updatedOpenedReactTabs[0] });
|
|
}
|
|
|
|
set({ openedReactTabs: updatedOpenedReactTabs });
|
|
},
|
|
setIsTabExecuting: (state: boolean) => {
|
|
set({ isTabExecuting: state });
|
|
},
|
|
setIsQueryErrorThrown: (state: boolean) => {
|
|
set({ isQueryErrorThrown: state });
|
|
},
|
|
getCurrentTabIndex: () => {
|
|
const state = get();
|
|
if (state.activeReactTab !== undefined) {
|
|
return state.openedReactTabs.indexOf(state.activeReactTab);
|
|
} else if (state.activeTab !== undefined) {
|
|
const nonReactTabIndex = state.openedTabs.indexOf(state.activeTab);
|
|
if (nonReactTabIndex !== -1) {
|
|
return state.openedReactTabs.length + nonReactTabIndex;
|
|
}
|
|
}
|
|
|
|
return -1;
|
|
},
|
|
selectTabByIndex: (index: number) => {
|
|
const state = get();
|
|
const totalTabCount = state.openedReactTabs.length + state.openedTabs.length;
|
|
const clampedIndex = clamp(index, totalTabCount - 1, 0);
|
|
|
|
if (clampedIndex < state.openedReactTabs.length) {
|
|
set({ activeTab: undefined, activeReactTab: state.openedReactTabs[clampedIndex] });
|
|
} else {
|
|
set({ activeTab: state.openedTabs[clampedIndex - state.openedReactTabs.length], activeReactTab: undefined });
|
|
}
|
|
},
|
|
selectLeftTab: () => {
|
|
const state = get();
|
|
state.selectTabByIndex(state.getCurrentTabIndex() - 1);
|
|
},
|
|
selectRightTab: () => {
|
|
const state = get();
|
|
state.selectTabByIndex(state.getCurrentTabIndex() + 1);
|
|
},
|
|
closeActiveTab: () => {
|
|
const state = get();
|
|
if (state.activeReactTab !== undefined) {
|
|
state.closeReactTab(state.activeReactTab);
|
|
} else if (state.activeTab !== undefined) {
|
|
state.closeTab(state.activeTab);
|
|
}
|
|
},
|
|
closeAllTabs: () => {
|
|
set({ openedTabs: [], openedReactTabs: [], activeTab: undefined, activeReactTab: undefined });
|
|
},
|
|
persistTabsState: () => {
|
|
const state = get();
|
|
const openTabsStates = state.openedTabs.map((tab) => tab.getPersistedState());
|
|
|
|
saveSubComponentState<OpenTab[]>(
|
|
AppStateComponentNames.DataExplorerAction,
|
|
OPEN_TABS_SUBCOMPONENT_NAME,
|
|
undefined,
|
|
openTabsStates,
|
|
);
|
|
},
|
|
}));
|