2025-04-01 10:38:02 +09:00

185 lines
5.3 KiB
TypeScript

import {
SceneObjectBase,
SceneObjectState,
SceneObjectUrlSyncConfig,
SceneObjectUrlValues,
VizPanel,
} from '@grafana/scenes';
import { t } from 'app/core/internationalization';
import { DashboardLayoutManager } from '../types/DashboardLayoutManager';
import { LayoutRegistryItem } from '../types/LayoutRegistryItem';
import { TabItem } from './TabItem';
import { TabsLayoutManagerRenderer } from './TabsLayoutManagerRenderer';
interface TabsLayoutManagerState extends SceneObjectState {
tabs: TabItem[];
currentTabIndex: number;
}
export class TabsLayoutManager extends SceneObjectBase<TabsLayoutManagerState> implements DashboardLayoutManager {
public static Component = TabsLayoutManagerRenderer;
public readonly isDashboardLayoutManager = true;
public static readonly descriptor: LayoutRegistryItem = {
get name() {
return t('dashboard.tabs-layout.name', 'Tabs');
},
get description() {
return t('dashboard.tabs-layout.description', 'Tabs layout');
},
id: 'tabs-layout',
createFromLayout: TabsLayoutManager.createFromLayout,
kind: 'TabsLayout',
};
public readonly descriptor = TabsLayoutManager.descriptor;
protected _urlSync = new SceneObjectUrlSyncConfig(this, { keys: ['tab'] });
public constructor(state: Partial<TabsLayoutManagerState>) {
super({
...state,
tabs: state.tabs ?? [new TabItem()],
currentTabIndex: state.currentTabIndex ?? 0,
});
}
public getUrlState() {
return { tab: this.state.currentTabIndex.toString() };
}
public updateFromUrl(values: SceneObjectUrlValues) {
if (!values.tab) {
return;
}
if (typeof values.tab === 'string') {
this.setState({ currentTabIndex: parseInt(values.tab, 10) });
}
}
public getCurrentTab(): TabItem {
return this.state.tabs.length > this.state.currentTabIndex
? this.state.tabs[this.state.currentTabIndex]
: this.state.tabs[0];
}
public addPanel(vizPanel: VizPanel) {
this.getCurrentTab().getLayout().addPanel(vizPanel);
}
public getVizPanels(): VizPanel[] {
const panels: VizPanel[] = [];
for (const tab of this.state.tabs) {
const innerPanels = tab.getLayout().getVizPanels();
panels.push(...innerPanels);
}
return panels;
}
public cloneLayout(ancestorKey: string, isSource: boolean): DashboardLayoutManager {
throw new Error('Method not implemented.');
}
public hasVizPanels(): boolean {
for (const tab of this.state.tabs) {
if (tab.getLayout().hasVizPanels()) {
return true;
}
}
return false;
}
public addNewTab() {
const currentTab = new TabItem();
this.setState({ tabs: [...this.state.tabs, currentTab], currentTabIndex: this.state.tabs.length });
}
public editModeChanged(isEditing: boolean) {
this.state.tabs.forEach((tab) => tab.getLayout().editModeChanged?.(isEditing));
}
public activateRepeaters() {
this.state.tabs.forEach((tab) => tab.getLayout().activateRepeaters?.());
}
public removeTab(tabToRemove: TabItem) {
// Do not allow removing last tab (for now)
if (this.state.tabs.length === 1) {
return;
}
const currentTab = this.getCurrentTab();
if (currentTab === tabToRemove) {
const nextTabIndex = this.state.currentTabIndex > 0 ? this.state.currentTabIndex - 1 : 0;
this.setState({ tabs: this.state.tabs.filter((t) => t !== tabToRemove), currentTabIndex: nextTabIndex });
return;
}
const filteredTab = this.state.tabs.filter((tab) => tab !== tabToRemove);
const tabs = filteredTab.length === 0 ? [new TabItem()] : filteredTab;
this.setState({ tabs, currentTabIndex: 0 });
}
public addTabBefore(tab: TabItem) {
const newTab = new TabItem();
const tabs = this.state.tabs.slice();
tabs.splice(tabs.indexOf(tab), 0, newTab);
this.setState({ tabs, currentTabIndex: this.state.currentTabIndex });
}
public addTabAfter(tab: TabItem) {
const newTab = new TabItem();
const tabs = this.state.tabs.slice();
tabs.splice(tabs.indexOf(tab) + 1, 0, newTab);
this.setState({ tabs, currentTabIndex: this.state.currentTabIndex + 1 });
}
public moveTabLeft(tab: TabItem) {
const currentIndex = this.state.tabs.indexOf(tab);
if (currentIndex <= 0) {
return;
}
const tabs = this.state.tabs.slice();
tabs.splice(currentIndex, 1);
tabs.splice(currentIndex - 1, 0, tab);
this.setState({ tabs, currentTabIndex: this.state.currentTabIndex - 1 });
}
public moveTabRight(tab: TabItem) {
const currentIndex = this.state.tabs.indexOf(tab);
if (currentIndex >= this.state.tabs.length - 1) {
return;
}
const tabs = this.state.tabs.slice();
tabs.splice(currentIndex, 1);
tabs.splice(currentIndex + 1, 0, tab);
this.setState({ tabs, currentTabIndex: this.state.currentTabIndex + 1 });
}
public isFirstTab(tab: TabItem): boolean {
return this.state.tabs[0] === tab;
}
public isLastTab(tab: TabItem): boolean {
return this.state.tabs[this.state.tabs.length - 1] === tab;
}
public static createEmpty(): TabsLayoutManager {
const tab = new TabItem();
return new TabsLayoutManager({ tabs: [tab] });
}
public static createFromLayout(layout: DashboardLayoutManager): TabsLayoutManager {
const tab = new TabItem({ layout: layout.clone() });
return new TabsLayoutManager({ tabs: [tab] });
}
}