import { useMemo } from 'react'; import { useObservable } from 'react-use'; import { type ComponentTypeWithExtensionMeta, type PluginExtensionComponentMeta, PluginExtensionTypes, usePluginContext, } from '@grafana/data'; import { UsePluginComponentsOptions, UsePluginComponentsResult } from '@grafana/runtime'; import { useAddedComponentsRegistry } from './ExtensionRegistriesContext'; import { AddedComponentRegistryItem } from './registry/AddedComponentsRegistry'; import { useLoadAppPlugins } from './useLoadAppPlugins'; import { generateExtensionId, getExtensionPointPluginDependencies } from './utils'; import { validateExtensionPoint } from './validateExtensionPoint'; // Returns an array of component extensions for the given extension point export function usePluginComponents({ limitPerPlugin, extensionPointId, }: UsePluginComponentsOptions): UsePluginComponentsResult { const registry = useAddedComponentsRegistry(); const registryState = useObservable(registry.asObservable()); const pluginContext = usePluginContext(); const { isLoading: isLoadingAppPlugins } = useLoadAppPlugins(getExtensionPointPluginDependencies(extensionPointId)); return useMemo(() => { const { result } = validateExtensionPoint({ extensionPointId, pluginContext, isLoadingAppPlugins }); if (result) { return { isLoading: result.isLoading, components: [], }; } const components: Array> = []; const extensionsByPlugin: Record = {}; for (const registryItem of registryState?.[extensionPointId] ?? []) { const { pluginId } = registryItem; // Only limit if the `limitPerPlugin` is set if (limitPerPlugin && extensionsByPlugin[pluginId] >= limitPerPlugin) { continue; } if (extensionsByPlugin[pluginId] === undefined) { extensionsByPlugin[pluginId] = 0; } const component = createComponentWithMeta( registryItem as AddedComponentRegistryItem, extensionPointId ); components.push(component); extensionsByPlugin[pluginId] += 1; } return { isLoading: false, components, }; }, [extensionPointId, limitPerPlugin, pluginContext, registryState, isLoadingAppPlugins]); } export function createComponentWithMeta( registryItem: AddedComponentRegistryItem, extensionPointId: string ): ComponentTypeWithExtensionMeta { const { component: Component, ...config } = registryItem; function ComponentWithMeta(props: Props) { return ; } ComponentWithMeta.displayName = Component.displayName; ComponentWithMeta.defaultProps = Component.defaultProps; ComponentWithMeta.propTypes = Component.propTypes; ComponentWithMeta.contextTypes = Component.contextTypes; ComponentWithMeta.meta = { pluginId: config.pluginId, title: config.title ?? '', description: config.description ?? '', id: generateExtensionId(config.pluginId, extensionPointId, config.title), type: PluginExtensionTypes.component, } satisfies PluginExtensionComponentMeta; return ComponentWithMeta; }