import type { PropertyPaneConfig } from "constants/PropertyControlConstants"; import type React from "react"; import type { WidgetBuilder, WidgetProps, WidgetState, } from "widgets/BaseWidget"; import type { RenderMode } from "constants/WidgetConstants"; import type { Stylesheet } from "entities/AppTheming"; import * as log from "loglevel"; import type { WidgetConfigProps } from "reducers/entityReducers/widgetConfigReducer"; import type { AutocompletionDefinitions, AutoLayoutConfig, CanvasWidgetStructure, WidgetMethods, } from "widgets/constants"; import { addPropertyConfigIds, addSearchConfigToPanelConfig, convertFunctionsToString, enhancePropertyPaneConfig, generatePropertyPaneSearchConfig, PropertyPaneConfigTypes, } from "./WidgetFactoryHelpers"; import type { WidgetFeatures } from "./WidgetFeatures"; import { FILL_WIDGET_MIN_WIDTH } from "constants/minWidthConstants"; type WidgetDerivedPropertyType = any; export type DerivedPropertiesMap = Record; export type WidgetType = (typeof WidgetFactory.widgetTypes)[number]; export enum NonSerialisableWidgetConfigs { CANVAS_HEIGHT_OFFSET = "canvasHeightOffset", } class WidgetFactory { static widgetTypes: Record = {}; static widgetMap: Map< WidgetType, WidgetBuilder > = new Map(); static widgetDerivedPropertiesGetterMap: Map< WidgetType, WidgetDerivedPropertyType > = new Map(); static derivedPropertiesMap: Map = new Map(); static defaultPropertiesMap: Map> = new Map(); static metaPropertiesMap: Map> = new Map(); static propertyPaneConfigsMap: Map< WidgetType, readonly PropertyPaneConfig[] > = new Map(); static propertyPaneContentConfigsMap: Map< WidgetType, readonly PropertyPaneConfig[] > = new Map(); static propertyPaneStyleConfigsMap: Map< WidgetType, readonly PropertyPaneConfig[] > = new Map(); // used to store the properties that appear in the search results static propertyPaneSearchConfigsMap: Map< WidgetType, readonly PropertyPaneConfig[] > = new Map(); static loadingProperties: Map> = new Map(); static stylesheetConfigMap: Map = new Map(); static autocompleteDefinitions: Map = new Map(); static widgetConfigMap: Map< WidgetType, Partial & WidgetConfigProps & { type: string } > = new Map(); static nonSerialisableWidgetConfigMap: Map< WidgetType, Record > = new Map(); static autoLayoutConfigMap: Map = new Map(); static widgetMethodsMap: Map> = new Map(); static registerWidgetBuilder( widgetType: string, widgetBuilder: WidgetBuilder, derivedPropertiesMap: DerivedPropertiesMap, defaultPropertiesMap: Record, metaPropertiesMap: Record, propertyPaneConfig?: PropertyPaneConfig[], propertyPaneContentConfig?: PropertyPaneConfig[], propertyPaneStyleConfig?: PropertyPaneConfig[], features?: WidgetFeatures, loadingProperties?: Array, stylesheetConfig?: Stylesheet, autocompleteDefinitions?: AutocompletionDefinitions, autoLayoutConfig?: AutoLayoutConfig, ) { if (!this.widgetTypes[widgetType]) { this.widgetTypes[widgetType] = widgetType; this.widgetMap.set(widgetType, widgetBuilder); this.derivedPropertiesMap.set(widgetType, derivedPropertiesMap); this.defaultPropertiesMap.set( widgetType, defaultPropertiesMap as Record, ); this.metaPropertiesMap.set(widgetType, metaPropertiesMap); loadingProperties && this.loadingProperties.set(widgetType, loadingProperties); stylesheetConfig && this.stylesheetConfigMap.set(widgetType, stylesheetConfig); autocompleteDefinitions && this.autocompleteDefinitions.set(widgetType, autocompleteDefinitions); if (Array.isArray(propertyPaneConfig) && propertyPaneConfig.length > 0) { const enhancedPropertyPaneConfig = enhancePropertyPaneConfig( propertyPaneConfig, features, ); const serializablePropertyPaneConfig = convertFunctionsToString( enhancedPropertyPaneConfig, ); const finalPropertyPaneConfig = addPropertyConfigIds( serializablePropertyPaneConfig, ); this.propertyPaneConfigsMap.set( widgetType, Object.freeze(finalPropertyPaneConfig), ); } if (propertyPaneContentConfig) { const enhancedPropertyPaneConfig = enhancePropertyPaneConfig( propertyPaneContentConfig, features, PropertyPaneConfigTypes.CONTENT, widgetType, ); const serializablePropertyPaneConfig = convertFunctionsToString( enhancedPropertyPaneConfig, ); const propertyPaneConfigWithIds = addPropertyConfigIds( serializablePropertyPaneConfig, ); const finalPropertyPaneConfig = addSearchConfigToPanelConfig( propertyPaneConfigWithIds, ); this.propertyPaneContentConfigsMap.set( widgetType, Object.freeze(finalPropertyPaneConfig), ); } if (propertyPaneStyleConfig) { const enhancedPropertyPaneConfig = enhancePropertyPaneConfig( propertyPaneStyleConfig, features, PropertyPaneConfigTypes.STYLE, ); const serializablePropertyPaneConfig = convertFunctionsToString( enhancedPropertyPaneConfig, ); const propertyPaneConfigWithIds = addPropertyConfigIds( serializablePropertyPaneConfig, ); const finalPropertyPaneConfig = addSearchConfigToPanelConfig( propertyPaneConfigWithIds, ); this.propertyPaneStyleConfigsMap.set( widgetType, Object.freeze(finalPropertyPaneConfig), ); } this.propertyPaneSearchConfigsMap.set( widgetType, generatePropertyPaneSearchConfig( WidgetFactory.getWidgetPropertyPaneContentConfig(widgetType), WidgetFactory.getWidgetPropertyPaneStyleConfig(widgetType), ), ); autoLayoutConfig && this.autoLayoutConfigMap.set(widgetType, { ...autoLayoutConfig, widgetSize: autoLayoutConfig.widgetSize?.map((sizeConfig) => ({ ...sizeConfig, configuration: (props: WidgetProps) => { if (!props) return { minWidth: this.widgetConfigMap.get(widgetType)?.minWidth || FILL_WIDGET_MIN_WIDTH, minHeight: this.widgetConfigMap.get(widgetType)?.minHeight || 80, }; return sizeConfig.configuration(props); }, })) || [], autoDimension: autoLayoutConfig.autoDimension ?? {}, disabledPropsDefaults: autoLayoutConfig.disabledPropsDefaults ?? {}, }); } } static storeWidgetConfig( widgetType: string, config: Partial & WidgetConfigProps & { type: string }, ) { this.widgetConfigMap.set(widgetType, Object.freeze(config)); } static storeNonSerialisablewidgetConfig( widgetType: string, config: Record, ) { this.nonSerialisableWidgetConfigMap.set(widgetType, config); } static createWidget( widgetData: CanvasWidgetStructure, renderMode: RenderMode, ): React.ReactNode { const widgetProps = { key: widgetData.widgetId, isVisible: true, ...widgetData, renderMode, }; const widgetBuilder = this.widgetMap.get(widgetData.type); if (widgetBuilder) { const widget = widgetBuilder.buildWidget(widgetProps); return widget; } else { const ex: WidgetCreationException = { message: "Widget Builder not registered for widget type" + widgetData.type, }; log.error(ex); return null; } } static getWidgetTypes(): WidgetType[] { return Array.from(this.widgetMap.keys()); } static getWidgetDerivedPropertiesMap( widgetType: WidgetType, ): DerivedPropertiesMap { const map = this.derivedPropertiesMap.get(widgetType); if (!map) { log.error("Widget type validation is not defined"); return {}; } return map; } static getWidgetDefaultPropertiesMap( widgetType: WidgetType, ): Record { const map = this.defaultPropertiesMap.get(widgetType); if (!map) { log.error("Widget default properties not defined", widgetType); return {}; } return map; } static getWidgetMetaPropertiesMap( widgetType: WidgetType, ): Record { const map = this.metaPropertiesMap.get(widgetType); if (!map) { log.error("Widget meta properties not defined: ", widgetType); return {}; } return map; } static getWidgetPropertyPaneCombinedConfig( type: WidgetType, ): readonly PropertyPaneConfig[] { const contentConfig = this.propertyPaneContentConfigsMap.get(type) || []; const styleConfig = this.propertyPaneStyleConfigsMap.get(type) || []; return [...contentConfig, ...styleConfig]; } static getWidgetPropertyPaneConfig( type: WidgetType, ): readonly PropertyPaneConfig[] { const map = this.propertyPaneConfigsMap.get(type); if (!map || (map && map.length === 0)) { const config = WidgetFactory.getWidgetPropertyPaneCombinedConfig(type); if (config === undefined) { log.error("Widget property pane config not defined", type); } return config; } return map; } static getWidgetPropertyPaneContentConfig( type: WidgetType, ): readonly PropertyPaneConfig[] { const map = this.propertyPaneContentConfigsMap.get(type); if (!map) { return []; } return map; } static getWidgetPropertyPaneStyleConfig( type: WidgetType, ): readonly PropertyPaneConfig[] { const map = this.propertyPaneStyleConfigsMap.get(type); if (!map) { return []; } return map; } static getWidgetPropertyPaneSearchConfig( type: WidgetType, ): readonly PropertyPaneConfig[] { const map = this.propertyPaneSearchConfigsMap.get(type); if (!map) { return []; } return map; } static getWidgetAutoLayoutConfig(type: WidgetType): AutoLayoutConfig { const map = this.autoLayoutConfigMap.get(type); if (!map) { return { autoDimension: {}, widgetSize: [], disableResizeHandles: {}, disabledPropsDefaults: {}, }; } return map; } static getWidgetTypeConfigMap(): WidgetTypeConfigMap { const typeConfigMap: WidgetTypeConfigMap = {}; WidgetFactory.getWidgetTypes().forEach((type) => { typeConfigMap[type] = { defaultProperties: WidgetFactory.getWidgetDefaultPropertiesMap(type), derivedProperties: WidgetFactory.getWidgetDerivedPropertiesMap(type), metaProperties: WidgetFactory.getWidgetMetaPropertiesMap(type), }; }); return typeConfigMap; } static getAutocompleteDefinitions( type: WidgetType, ): AutocompletionDefinitions | undefined { const autocompleteDefinition = this.autocompleteDefinitions.get(type); if (!autocompleteDefinition) { log.error("Widget autocomplete properties not defined: ", type); } return autocompleteDefinition; } static getLoadingProperties(type: WidgetType): Array | undefined { return this.loadingProperties.get(type); } static getWidgetStylesheetConfigMap(widgetType: WidgetType) { const map = this.stylesheetConfigMap.get(widgetType); if (!map) { log.error("Widget stylesheet properties not defined: ", widgetType); return undefined; } return map; } static setWidgetMethods( type: WidgetType, methods: Record, ) { this.widgetMethodsMap.set(type, methods); } static getWidgetMethods(type: WidgetType) { const methods = this.widgetMethodsMap.get(type); if (!methods) { log.error("Widget methods are not defined: ", type); return {}; } return methods; } } export type WidgetTypeConfigMap = Record< string, { defaultProperties: Record; metaProperties: Record; derivedProperties: WidgetDerivedPropertyType; } >; export interface WidgetCreationException { message: string; } export default WidgetFactory;