/** * @fileoverview Angular component that hosts React components with proper memory management. * Provides a bridge between Angular and React ecosystems in MemberJunction applications. * @module @memberjunction/ng-react */ import { EventEmitter, ElementRef, AfterViewInit, OnDestroy, ChangeDetectorRef } from '@angular/core'; import { BaseAngularComponent } from '@memberjunction/ng-base-types'; import { ComponentSpec, ComponentStyles } from '@memberjunction/interactive-component-types'; import { ReactBridgeService } from '../services/react-bridge.service'; import { AngularAdapterService } from '../services/angular-adapter.service'; import { CompositeKey, DataSnapshot } from '@memberjunction/core'; import { MJNotificationService } from '@memberjunction/ng-notifications'; import * as i0 from "@angular/core"; /** * Event emitted by React components */ export interface ReactComponentEvent { type: string; payload: any; } /** * State change event emitted when component state updates */ export interface StateChangeEvent { path: string; value: any; } /** * User settings changed event emitted when component saves user preferences */ export interface UserSettingsChangedEvent { settings: Record; componentName?: string; timestamp: Date; } /** * Angular component that hosts React components with proper memory management. * This component provides a bridge between Angular and React, allowing React components * to be used seamlessly within Angular applications. */ export declare class MJReactComponent extends BaseAngularComponent implements AfterViewInit, OnDestroy { private reactBridge; private adapter; private cdr; private notificationService; private _component; /** * The component specification to render. * When this changes after initialization, the component will be reinitialized * to load and render the new specification. */ set component(value: ComponentSpec); get component(): ComponentSpec; /** * Controls verbose logging for component lifecycle and operations. * Note: This does NOT control which React build (dev/prod) is loaded. * To control React builds, use ReactDebugConfig.setDebugMode() at app startup. */ enableLogging: boolean; useComponentManager: boolean; private _utilities; set utilities(value: any); get utilities(): any; private _styles?; private _themeStyles?; private _themeStylesKey?; private themeObserver?; set styles(value: Partial | undefined); get styles(): Partial; /** * Explicitly user-requested styling from the spec being rendered, resolved above * the theme. Reads the resolved spec when available (registry components resolve * asynchronously) and falls back to the spec passed in. */ private get styleOverrides(); private _mergedStylesBase?; private _mergedStylesOverrides?; private _mergedStylesResult?; private applyStyleOverridesMemoized; /** * Identifies the current theme so bridged styles can be memoized and refreshed * when the user's mode (`data-theme`) or the org overlay (`data-theme-overlay`) * changes. Non-DOM environments return a constant key. */ private computeThemeKey; /** * Watches the document root for theme changes (`data-theme` / `data-theme-overlay`) * and, when the theme flips, invalidates the bridged-styles cache and re-renders so * the live React component picks up the new theme. No-op when styles are supplied * explicitly or when there is no DOM. */ private setupThemeObserver; private _savedUserSettings; set savedUserSettings(value: any); get savedUserSettings(): any; /** * Optional explicit scope for per-user settings persistence. When omitted, the * scope defaults to `/` of the component spec. Settings are * stored per-user via `UserInfoEngine` under the key * `InteractiveComponents_UserState_Root/`. Provide an * explicit scope when a single component spec is rendered in multiple distinct * contexts that should NOT share preferences (e.g. the same form spec used for * different entities) — set it to something stable and unique per context. */ UserStateScope?: string; /** * When `true` (default), the host transparently persists `savedUserSettings` * per-user, cross-device via `UserInfoEngine` — seeding the component from * storage on load and saving (debounced) on every `onSaveUserSettings` call, * auto-scoped per component. Set to `false` to opt out and own persistence * yourself by handling the `userSettingsChanged` output instead. */ PersistUserSettings: boolean; /** * Host-supplied props spread into the React component's props alongside the * standard `utilities`, `callbacks`, `components`, `styles`, `libraries`, and * `savedUserSettings`. Used by hosts that need to push data context the React * component can't fetch itself — e.g. `InteractiveFormComponent` passing * `FormHostProps` (the current record snapshot, mode, permissions). * * Standard keys take precedence over caller-supplied keys to keep the * platform contract stable. */ private _componentProps; set componentProps(value: object | undefined); get componentProps(): object; stateChange: EventEmitter; componentEvent: EventEmitter; refreshData: EventEmitter; openEntityRecord: EventEmitter<{ entityName: string; key: CompositeKey; }>; userSettingsChanged: EventEmitter; /** Emitted once after the component successfully loads and resolvedComponentSpec is populated. */ initialized: EventEmitter; container: ElementRef; private capturedData; private reactRootId; private compiledComponent; private loadedDependencies; private destroyed$; private currentCallbacks; isInitialized: boolean; private isRendering; private pendingRender; private isDestroying; private componentId; private componentVersion; hasError: boolean; /** * Public property containing the fully resolved component specification. * This includes all external code fetched from registries, allowing consumers * to inspect the complete resolved specification including dependencies. * Only populated after successful component initialization. */ resolvedComponentSpec: ComponentSpec | null; constructor(reactBridge: ReactBridgeService, adapter: AngularAdapterService, cdr: ChangeDetectorRef, notificationService: MJNotificationService); ngAfterViewInit(): Promise; ngOnDestroy(): void; /** * Reinitialize the component when the input spec changes. * Cleans up the current component and initializes with the new spec. */ private reinitializeComponent; /** * Initialize the React component */ private initializeComponent; /** * Generate a hash from component code for versioning * Uses a simple hash function that's fast and sufficient for version differentiation */ private generateComponentHash; /** * Resolve components using the runtime's resolver */ private resolveComponentsWithVersion; /** * NEW: Load component using unified ComponentManager - MUCH SIMPLER! */ private loadComponentWithManager; /** * Register all components in the hierarchy * @deprecated Use loadComponentWithManager() instead */ private registerComponentHierarchy; /** * Post-process resolved spec to ensure all components show their true registry source. * This enriches the spec for UI display purposes to show where components actually came from. * Applied to all resolved specs so any consumer of this wrapper benefits. */ private enrichSpecWithRegistryInfo; /** * Render the React component */ private renderComponent; /** * Create callbacks for the React component */ private createCallbacks; /** * Handle React component errors */ private handleReactError; /** * Handle onSaveUserSettings from components. * * This implements the SavedUserSettings pattern: the component owns its single * settings object and hands us the full latest copy whenever it changes. We * (1) **merge** the payload over our in-memory snapshot so any future re-render * passes the latest values, (2) persist the merged snapshot per-user via * UserInfoEngine (debounced, auto-scoped) unless the host opted out, and * (3) still bubble the event up for any parent container that wants to observe * changes — carrying the merged snapshot, so observers and storage agree. * * Merge (not replace) makes the host resilient to a component passing only the * changed keys, and to the stale-prop case: we deliberately never re-render on * save, so the `savedUserSettings` prop a component spreads is frozen at mount * and would otherwise lose earlier same-session changes. Removing a key * requires explicit intent — set it to `null` (see applyUserSettingsUpdate). */ private handleSaveUserSettings; /** * Resolve the durable storage key for this component's per-user settings, or * null when persistence is disabled or no stable scope can be derived. */ private getUserStateStorageKey; /** * Seed `savedUserSettings` from durable per-user storage (UserInfoEngine), * merging stored values over any host-provided defaults (stored wins). Best * effort — any failure leaves the host-provided / empty settings in place. */ private seedUserSettingsFromStore; /** * Persist the component's settings object for the current user (debounced, * cross-device). Best effort — failures are logged when logging is enabled and * never surfaced to the component. */ private persistUserSettings; /** * Wraps a ComponentUtilities object so that every RunView / RunViews / RunQuery * call transparently stores the result in `this.capturedData`. The wrapped * object is referentially distinct from the original and is safe to pass to * multiple renders (results accumulate until the component resets). */ private wrapUtilitiesWithCapture; /** * Builds a DataSnapshot from captured RunView/RunQuery results. * Returns null if no data was captured. */ private BuildCapturedDataSnapshot; /** * Clean up resources */ private cleanup; /** * Public method to refresh the component * @deprecated Components manage their own state and data now */ refresh(): void; /** * Public method to update state programmatically * @param path - State path to update * @param value - New value * @deprecated Components manage their own state now */ updateState(path: string, value: any): void; /** * Gets the current data state of the component. * Tries the component's explicit implementation first, then falls back * to a DataSnapshot built from intercepted RunView/RunQuery results. */ getCurrentDataState(): DataSnapshot | undefined; /** * Validates the current state of the component * @returns true if valid, false or validation errors otherwise */ validate(): boolean | { valid: boolean; errors?: string[]; }; /** * Checks if the component has unsaved changes * @returns true if dirty, false otherwise */ isDirty(): boolean; /** * Resets the component to its initial state */ reset(): void; /** * Scrolls to a specific element or position within the component * @param target - Element selector, element reference, or scroll options */ scrollTo(target: string | HTMLElement | { top?: number; left?: number; }): void; /** * Sets focus to a specific element within the component * @param target - Element selector or element reference */ focus(target?: string | HTMLElement): void; /** * Invokes a custom method on the component * @param methodName - Name of the method to invoke * @param args - Arguments to pass to the method * @returns The result of the method call, or undefined if method doesn't exist */ invokeMethod(methodName: string, ...args: any[]): any; /** * Checks if a method is available on the component * @param methodName - Name of the method to check * @returns true if the method exists */ hasMethod(methodName: string): boolean; /** * Print the component content * Uses component's print method if available, otherwise uses window.print() */ print(): void; /** * Force clear component registries * Used by Component Studio for fresh loads * This is a static method that can be called without a component instance */ static forceClearRegistries(): void; /** * Gets the current data state from the hosted React component. * Falls back to a DataSnapshot built from intercepted RunView/RunQuery results * when the component does not explicitly implement getCurrentDataState. */ GetCurrentDataState(): DataSnapshot | undefined; /** * Applies a data state snapshot to the hosted React component. * Returns true if the snapshot was successfully applied, false if the * component does not support setDataState or the operation failed. */ SetDataState(snapshot: DataSnapshot): boolean; static ɵfac: i0.ɵɵFactoryDeclaration; static ɵcmp: i0.ɵɵComponentDeclaration; }