Skip to content

Commit

Permalink
feat: widget registry service & types
Browse files Browse the repository at this point in the history
  • Loading branch information
rahul-rocket committed Aug 29, 2024
1 parent 817902d commit eb104fc
Show file tree
Hide file tree
Showing 4 changed files with 220 additions and 0 deletions.
1 change: 1 addition & 0 deletions packages/ui-core/core/src/lib/services/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -68,3 +68,4 @@ export * from './warehouse';
export * from './page';
export * from './store';
export * from './time-tracker';
export * from './widget';
2 changes: 2 additions & 0 deletions packages/ui-core/core/src/lib/services/widget/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export * from './widget-registry.service';
export * from './widget-registry.types';
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
import { Injectable } from '@angular/core';
import { IWidgetRegistry, WidgetPageLocationId, WidgetRegistryConfig } from './widget-registry.types';

@Injectable({
providedIn: 'root'
})
export class WidgetRegistryService implements IWidgetRegistry {
/**
* @description
* Registry for storing page widget configurations.
*
* This Map stores arrays of WidgetRegistryConfig objects, keyed by WidgetPageLocationId.
*/
private readonly registry = new Map<WidgetPageLocationId, WidgetRegistryConfig[]>();

/**
* Retrieves the current widget registry.
*
* This method returns a map of widget configurations, organized by their page locations.
*
* @returns A `Map` where each key is a `WidgetPageLocationId` and each value is an array of
* `WidgetRegistryConfig` objects associated with that page location.
*/
public getRegistry(): ReadonlyMap<WidgetPageLocationId, WidgetRegistryConfig[]> {
return new Map(this.registry); // Return a new Map to ensure immutability
}

/**
* Registers a single widget with the service.
*
* This method is responsible for registering a widget by adding its configuration
* to the widget registry. It ensures that the widget configuration includes the
* necessary properties (`widgetId` and `location`) before proceeding to add it
* to the registry. If any of these properties are missing, it throws an error.
*
* @param config - The configuration object for the widget to be registered.
* @throws Error - Throws an error if the `widgetId` or `location` properties are missing.
*/
public registerWidget(config: WidgetRegistryConfig): void {
// Ensure the widget configuration includes a location.
if (!config.location) {
throw new Error('A widget configuration must have a location property');
}

// Ensure the widget configuration includes a unique identifier.
if (!config.widgetId) {
throw new Error('A widget configuration must have a widgetId property');
}

// Retrieve the existing widgets for the specified location from the registry,
// or initialize an empty array if none exist.
const widgets = this.registry.get(config.location) || [];

// Check if a route with the same location and path already exists
const isMatchingWidget = widgets.some(
(widget: WidgetRegistryConfig) => widget.location === config.location && widget.widgetId === config.widgetId
);

// Check if a route with the same location already exists
if (isMatchingWidget) {
throw new Error(`Widget with id "${config.widgetId}" already exists at location "${config.location}"`);
}

// Add the new widget configuration to the list of widgets for the specified location
widgets.push(config);

// Update the registry with the new or updated list of widgets for the specified location.
this.registry.set(config.location, widgets);
}

/**
* Registers multiple widgets with the service.
*
* This method adds multiple widget configurations to the registry. It processes each
* configuration sequentially by calling `registerWidget` for each one.
*
* @param configs An array of configuration objects for the widgets to be registered. Each
* object in the array should follow the `WidgetRegistryConfig` schema.
* @throws Error if any widget ID is missing or if any widget ID already exists.
*/
public registerWidgets(configs: WidgetRegistryConfig[]): void {
configs.forEach((config: WidgetRegistryConfig) => this.registerWidget(config));
}

/**
* Retrieves the widgets registered at a specific location.
*
* @param location - The location for which to retrieve the widgets.
*
* @returns An array of `WidgetRegistryConfig` objects registered at the specified location.
* If no widgets are registered at the location, an empty array is returned.
*/
getLocationWidgets(location: WidgetPageLocationId): WidgetRegistryConfig[] {
return this.registry.get(location) || [];
}
}
121 changes: 121 additions & 0 deletions packages/ui-core/core/src/lib/services/widget/widget-registry.types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
import { Type } from '@angular/core';
import { ResolveFn } from '@angular/router';
import { PermissionsEnum } from '@gauzy/contracts';

/**
* The width of a widget in terms of grid columns.
*/
export type WidgetGridWidth = 3 | 4 | 6 | 8 | 12;

/**
* Enum representing the possible widget page locations.
*
* This enum is used to identify different sections of the application where widgets can be registered.
* Each value corresponds to a specific page or section in the application. This allows for flexible
* and dynamic registration based on the context and requirements of the application.
*
* @readonly
* @enum {string}
*/
export type WidgetPageLocationId = 'time-tracking' | 'accounting';

/**
* Configuration for registering a widget.
*/
export interface WidgetRegistryConfig {
/**
* @description
* The location of the widget in the application. This is used to determine
* where the widget should be rendered in the application.
*
* @example 'time-tracking'
*/
location: WidgetPageLocationId;

/**
* @description
* The unique identifier of the widget. This ID is used to distinguish
* the widget from others and can be used to reference or load the widget
* in the application.
*
* @example 'weekly-activity'
*/
widgetId: string;

/**
* @description
* The title of the widget. This title is typically used for display purposes,
* such as in headers or menus, to give users an understanding of the widget's
* purpose or content. The title can be:
*
* - A static string for a fixed title.
* - A resolver function that returns a promise or direct value of the title.
* - A translation key for a dynamic title.
*
* @example 'Time Tracking' // Static title
* @example () => Promise.resolve('Time Tracking') // Resolver function returning a promise
* @example () => 'Time Tracking' // Resolver function returning a direct value
*/
title?: string | ResolveFn<string>;

/**
* @description
* Function that returns a promise or a direct type of the component to be loaded.
* This function is used to dynamically load the component associated with the widget.
* It allows for lazy loading of components to optimize performance and reduce initial
* load time.
*
* @example
* () => import('./weekly-activity-widget.component').then(m => m.WeeklyActivityWidgetComponent)
*/
loadComponent?: () => Promise<Type<any>> | Type<any>;

/**
* @description
* Array of widths supported by the widget in the grid layout. Each width value
* corresponds to the number of columns the widget can span. This allows the widget
* to be responsive and adapt to different layout configurations.
*
* @example [3, 4, 6, 8, 12]
*/
supportedWidths?: WidgetGridWidth[];

/**
* @description
* Array of permissions required to view or use the widget. Each permission is
* represented as a string, and these permissions are checked to ensure that the
* user has the necessary rights to access or interact with the widget.
*
* @example ['admin', 'user']
*/
permissions: string[] | PermissionsEnum[];
}

/**
* Widget registry service interface.
*
* This interface defines the contract for services that manage widget registrations,
* including methods for registering single and multiple widgets.
*/
export interface IWidgetRegistry {
/**
* Registers a single widget with the service.
*
* This method adds a single widget configuration to the registry. If a widget with
* the same ID is already registered, an error will be thrown.
*
* @param config The configuration object for the widget to be registered.
*/
registerWidget(config: WidgetRegistryConfig): void;

/**
* Registers multiple widgets with the service.
*
* This method adds multiple widget configurations to the registry. If any widget
* within the provided configurations already exists, an error will be thrown for that
* specific widget, but other widgets will still be registered.
*
* @param configs An array of configuration objects for the widgets to be registered.
*/
registerWidgets(configs: WidgetRegistryConfig[]): void;
}

0 comments on commit eb104fc

Please sign in to comment.