mirror of
https://github.com/grafana/grafana.git
synced 2025-07-30 10:32:42 +08:00

* Add split view and basic APIs to extensions * Add comments * Update public/app/AppWrapper.tsx Co-authored-by: Levente Balogh <balogh.levente.hu@gmail.com> * Moved the .grafana-app element and deduplicate some code * Remove the provider variants of usePluginLinks/Components * Change buildPluginSectionNav * Update comment * Use eventBus * Remove non existent exports * refactor: use a sidecar service to encapsulate the state * Don't wrap single app in split wrapper * Use hook splitter * Remove inline styles * Type the style props from useSplitter * Move the overflow style changes to appWrapper * Deduplicate some common top level providers * Move modals * Move routes wrappers to it's own file * Use better css and add comments * Remove query rows app extension point * Fix test --------- Co-authored-by: Levente Balogh <balogh.levente.hu@gmail.com>
88 lines
2.9 KiB
TypeScript
88 lines
2.9 KiB
TypeScript
import { GrafanaPlugin, NavModel, NavModelItem, PanelPluginMeta, PluginType } from '@grafana/data';
|
|
import { createMonitoringLogger } from '@grafana/runtime';
|
|
|
|
import { importPanelPluginFromMeta } from './importPanelPlugin';
|
|
import { getPluginSettings } from './pluginSettings';
|
|
import { importAppPlugin, importDataSourcePlugin } from './plugin_loader';
|
|
|
|
export async function loadPlugin(pluginId: string): Promise<GrafanaPlugin> {
|
|
const info = await getPluginSettings(pluginId);
|
|
let result: GrafanaPlugin | undefined;
|
|
|
|
if (info.type === PluginType.app) {
|
|
result = await importAppPlugin(info);
|
|
}
|
|
if (info.type === PluginType.datasource) {
|
|
result = await importDataSourcePlugin(info);
|
|
}
|
|
if (info.type === PluginType.panel) {
|
|
const panelPlugin = await importPanelPluginFromMeta(info as PanelPluginMeta);
|
|
result = panelPlugin as unknown as GrafanaPlugin;
|
|
}
|
|
if (info.type === PluginType.renderer) {
|
|
result = { meta: info } as GrafanaPlugin;
|
|
}
|
|
|
|
if (!result) {
|
|
throw new Error('Unknown Plugin type: ' + info.type);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
export function buildPluginSectionNav(currentUrl: string, pluginNavSection?: NavModelItem): NavModel | undefined {
|
|
if (!pluginNavSection) {
|
|
return undefined;
|
|
}
|
|
// shallow clone as we set active flag
|
|
const MAX_RECURSION_DEPTH = 10;
|
|
let copiedPluginNavSection = { ...pluginNavSection };
|
|
let activePage: NavModelItem | undefined;
|
|
|
|
function setPageToActive(page: NavModelItem, currentUrl: string): NavModelItem {
|
|
if (!currentUrl.startsWith(page.url ?? '')) {
|
|
return page;
|
|
}
|
|
|
|
// Check if there is already an active page found with with a more specific url (possibly a child of the current page)
|
|
// (In this case we bail out early and don't mark the parent as active)
|
|
if (activePage && (activePage.url?.length ?? 0) > (page.url?.length ?? 0)) {
|
|
return page;
|
|
}
|
|
|
|
if (activePage) {
|
|
activePage.active = false;
|
|
}
|
|
|
|
activePage = { ...page, active: true };
|
|
|
|
return activePage;
|
|
}
|
|
|
|
function findAndSetActivePage(child: NavModelItem, depth = 0): NavModelItem {
|
|
if (depth > MAX_RECURSION_DEPTH) {
|
|
return child;
|
|
}
|
|
|
|
if (child.children) {
|
|
// Doing this here to make sure that first we check if any of the children is active
|
|
// (In case yes, then the check for the parent will not mark it as active)
|
|
const children = child.children.map((pluginPage) => findAndSetActivePage(pluginPage, depth + 1));
|
|
|
|
return {
|
|
...setPageToActive(child, currentUrl),
|
|
children,
|
|
};
|
|
}
|
|
|
|
return setPageToActive(child, currentUrl);
|
|
}
|
|
|
|
// Find and set active page
|
|
copiedPluginNavSection.children = (copiedPluginNavSection?.children ?? []).map(findAndSetActivePage);
|
|
|
|
return { main: copiedPluginNavSection, node: activePage ?? copiedPluginNavSection };
|
|
}
|
|
|
|
export const pluginsLogger = createMonitoringLogger('features.plugins');
|