UNPKG

@fleetbase/ember-core

Version:

Provides all the core services, decorators and utilities for building a Fleetbase extension for the Console.

373 lines (328 loc) 12.6 kB
import Service from '@ember/service'; import { inject as service } from '@ember/service'; import { warn } from '@ember/debug'; import { isArray } from '@ember/array'; import { tracked } from '@glimmer/tracking'; import Widget from '../../contracts/widget'; import isObject from '../../utils/is-object'; /** * WidgetManagerService * * Manages dashboard widgets and widget registrations. * * Widgets are registered per-dashboard: * - registerWidgets(dashboardName, widgets) - Makes widgets available for selection on a dashboard * - registerDefaultWidgets(dashboardName, widgets) - Auto-loads specific widgets on a dashboard * * Registry Structure: * - Dashboards: 'dashboards' section, 'dashboard' list * - Widgets: 'dashboard:widgets' section, 'widget' list * - Default Widgets: 'dashboard:widgets' section, 'default-widget' list * * @class WidgetService * @extends Service */ export default class WidgetService extends Service { @service('universe/registry-service') registry; /** * Reference to the root Ember Application Instance. * Used for registering components/services to the application container * for cross-engine sharing. */ @tracked applicationInstance = null; /** * Set the application instance (for consistency with other services) * * @method setApplicationInstance * @param {Application} application The root application instance */ setApplicationInstance(application) { this.applicationInstance = application; } /** * Normalize a widget input to a plain object * * @private * @method #normalizeWidget * @param {Widget|Object} input Widget instance or object * @returns {Object} Normalized widget object */ #normalizeWidget(input) { if (input instanceof Widget) { return input.toObject(); } // Handle plain objects - ensure id property exists if (isObject(input)) { // Support both id and widgetId for backward compatibility const id = input.id || input.widgetId; if (!id) { warn('[WidgetService] Widget definition is missing id or widgetId', { id: 'widget-service.missing-id' }); } return { ...input, id, // Ensure id property is set }; } return input; } /** * Register a dashboard * * @method registerDashboard * @param {String} name Dashboard name/ID * @param {Object} options Dashboard options */ registerDashboard(name, options = {}) { const dashboard = { name, ...options, }; // Register to 'dashboards' section, 'dashboard' list this.registry.register('dashboards', 'dashboard', name, dashboard); } /** * Register a dashboard render slot. * * Slots describe where dashboards render (for example `console.home`), * while dashboard names continue to describe widget namespaces. * * @method registerDashboardSlot * @param {String} slotId Slot identifier * @param {Object} options Slot options */ registerDashboardSlot(slotId, options = {}) { const slot = { id: slotId, ...options, }; this.registry.register('dashboard:slots', 'slot', slotId, slot); } /** * Register a system dashboard that should be available in a render slot. * * @method registerDashboardForSlot * @param {String} slotId Slot identifier * @param {String} dashboardName Dashboard widget namespace * @param {Object} options Dashboard slot options */ registerDashboardForSlot(slotId, dashboardName, options = {}) { this.registerDashboardSlot(slotId); this.registerDashboard(dashboardName, options); const dashboard = { id: dashboardName, dashboardId: dashboardName, name: options.name ?? options.defaultDashboardName ?? dashboardName, extension: options.extension ?? 'core', priority: options.priority ?? 0, ...options, }; this.registry.register('dashboard:slots', 'dashboard', `${slotId}#${dashboardName}`, { slotId, ...dashboard, }); } /** * Set the system dashboard that should load first for a render slot. * * @method setDefaultDashboardForSlot * @param {String} slotId Slot identifier * @param {String} dashboardName Dashboard widget namespace */ setDefaultDashboardForSlot(slotId, dashboardName) { this.registerDashboardSlot(slotId); this.registry.register('dashboard:slots', 'default', slotId, { slotId, dashboardId: dashboardName, }); } /** * Convenience alias for setting the console home dashboard. * * @method setConsoleDashboard * @param {String} dashboardName Dashboard widget namespace */ setConsoleDashboard(dashboardName) { this.setDefaultDashboardForSlot('console.home', dashboardName); } /** * Get system dashboards registered for a slot. * * @method getDashboardsForSlot * @param {String} slotId Slot identifier * @returns {Array} Slot dashboard registrations */ getDashboardsForSlot(slotId) { if (!slotId) { return []; } const prefix = `${slotId}#`; return [...this.registry.getRegistry('dashboard:slots', 'dashboard')] .filter((dashboard) => dashboard?._registryKey?.startsWith(prefix)) .sort((a, b) => (b.priority ?? 0) - (a.priority ?? 0)); } /** * Get the dashboard namespace that should load first for a slot. * * @method getDefaultDashboardForSlot * @param {String} slotId Slot identifier * @returns {String|null} Dashboard namespace */ getDefaultDashboardForSlot(slotId) { return this.registry.lookup('dashboard:slots', 'default', slotId)?.dashboardId ?? null; } /** * Register widgets to a specific dashboard * Makes these widgets available for selection on the dashboard * If a widget has `default: true`, it's also registered as a default widget * * @method registerWidgets * @param {String} dashboardName Dashboard name/ID * @param {Array<Widget>} widgets Array of widget instances or objects */ registerWidgets(dashboardName, widgets) { if (!isArray(widgets)) { widgets = [widgets]; } widgets.forEach((widget) => { const normalized = this.#normalizeWidget(widget); // Register widget to 'dashboard:widgets' section, 'widget' list // Key format: dashboardName#widgetId this.registry.register('dashboard:widgets', 'widget', `${dashboardName}#${normalized.id}`, normalized); // If marked as default, also register to default widget list if (normalized.default === true) { this.registry.register('dashboard:widgets', 'default-widget', `${dashboardName}#${normalized.id}`, normalized); } }); } /** * Register default widgets for a specific dashboard * These widgets are automatically loaded on the dashboard * * @method registerDefaultWidgets * @param {String} dashboardName Dashboard name/ID * @param {Array<Widget>} widgets Array of widget instances or objects */ registerDefaultWidgets(dashboardName, widgets) { if (!isArray(widgets)) { widgets = [widgets]; } widgets.forEach((widget) => { const normalized = this.#normalizeWidget(widget); // Register to 'dashboard:widgets' section, 'default-widget' list // Key format: dashboardName#widgetId this.registry.register('dashboard:widgets', 'default-widget', `${dashboardName}#${normalized.id}`, normalized); }); } /** * Get widgets for a specific dashboard * Returns all widgets available for selection on that dashboard * * @method getWidgets * @param {String} dashboardName Dashboard name/ID * @returns {Array} Widgets available for the dashboard */ getWidgets(dashboardName) { if (!dashboardName) { return []; } // Get all widgets from 'dashboard:widgets' section, 'widget' list const registry = this.registry.getRegistry('dashboard:widgets', 'widget'); // Filter widgets by registration key prefix const prefix = `${dashboardName}#`; return registry.filter((widget) => { if (!widget || typeof widget !== 'object') return false; // Match widgets registered for this dashboard return widget._registryKey && widget._registryKey.startsWith(prefix); }); } /** * Get default widgets for a specific dashboard * Returns widgets that should be auto-loaded * * @method getDefaultWidgets * @param {String} dashboardName Dashboard name/ID * @returns {Array} Default widgets for the dashboard */ getDefaultWidgets(dashboardName) { if (!dashboardName) { return []; } // Get all default widgets from 'dashboard:widgets' section, 'default-widget' list const registry = this.registry.getRegistry('dashboard:widgets', 'default-widget'); // Filter widgets by registration key prefix const prefix = `${dashboardName}#`; return registry.filter((widget) => { if (!widget || typeof widget !== 'object') return false; // Match default widgets registered for this dashboard return widget._registryKey && widget._registryKey.startsWith(prefix); }); } /** * Get a specific widget by ID from a dashboard * * @method getWidget * @param {String} dashboardName Dashboard name/ID * @param {String} widgetId Widget ID * @returns {Object|null} Widget or null */ getWidget(dashboardName, widgetId) { return this.registry.lookup('dashboard:widgets', 'widget', `${dashboardName}#${widgetId}`); } /** * Get all dashboards * * @method getDashboards * @returns {Array} All dashboards */ getDashboards() { return this.registry.getRegistry('dashboards', 'dashboard'); } /** * Get a specific dashboard * * @method getDashboard * @param {String} name Dashboard name * @returns {Object|null} Dashboard or null */ getDashboard(name) { return this.registry.lookup('dashboards', 'dashboard', name); } /** * Get registry for a specific dashboard * Used by dashboard models to get their widget registry * * @method getRegistry * @param {String} dashboardId Dashboard ID * @returns {Array} Widget registry for the dashboard */ getRegistry(dashboardId) { return this.getWidgets(dashboardId); } // ============================================================================ // DEPRECATED METHODS (for backward compatibility) // ============================================================================ /** * Register default dashboard widgets * DEPRECATED: Use registerDefaultWidgets(dashboardName, widgets) instead * * @method registerDefaultDashboardWidgets * @param {Array<Widget>} widgets Array of widget instances or objects * @deprecated Use registerDefaultWidgets('dashboard', widgets) instead */ registerDefaultDashboardWidgets(widgets) { warn('[WidgetService] registerDefaultDashboardWidgets is deprecated. Use registerDefaultWidgets(dashboardName, widgets) instead.', { id: 'widget-service.deprecated-method' }); this.registerDefaultWidgets('dashboard', widgets); } /** * Register dashboard widgets * DEPRECATED: Use registerWidgets(dashboardName, widgets) instead * * @method registerDashboardWidgets * @param {Array<Widget>} widgets Array of widget instances or objects * @deprecated Use registerWidgets('dashboard', widgets) instead */ registerDashboardWidgets(widgets) { warn('[WidgetService] registerDashboardWidgets is deprecated. Use registerWidgets(dashboardName, widgets) instead.', { id: 'widget-service.deprecated-method' }); this.registerWidgets('dashboard', widgets); } }