@memberjunction/ng-react 2.70.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,391 @@
1
+ /**
2
+ * @fileoverview Angular component that hosts React components with proper memory management.
3
+ * Provides a bridge between Angular and React ecosystems in MemberJunction applications.
4
+ * @module @memberjunction/ng-react
5
+ */
6
+ import { Component, Input, Output, EventEmitter, ViewChild, ElementRef, ChangeDetectionStrategy, ChangeDetectorRef } from '@angular/core';
7
+ import { Subject } from 'rxjs';
8
+ import { ReactBridgeService } from '../services/react-bridge.service';
9
+ import { AngularAdapterService } from '../services/angular-adapter.service';
10
+ import { buildComponentProps, createErrorBoundary, ComponentHierarchyRegistrar } from '@memberjunction/react-runtime';
11
+ import { LogError } from '@memberjunction/core';
12
+ import * as i0 from "@angular/core";
13
+ import * as i1 from "../services/react-bridge.service";
14
+ import * as i2 from "../services/angular-adapter.service";
15
+ const _c0 = ["container"];
16
+ function MJReactComponent_Conditional_3_Template(rf, ctx) { if (rf & 1) {
17
+ i0.ɵɵelementStart(0, "div", 3)(1, "div", 4);
18
+ i0.ɵɵelement(2, "i", 5);
19
+ i0.ɵɵelementEnd();
20
+ i0.ɵɵelementStart(3, "div", 6);
21
+ i0.ɵɵtext(4, "Loading component...");
22
+ i0.ɵɵelementEnd()();
23
+ } }
24
+ /**
25
+ * Angular component that hosts React components with proper memory management.
26
+ * This component provides a bridge between Angular and React, allowing React components
27
+ * to be used seamlessly within Angular applications.
28
+ */
29
+ export class MJReactComponent {
30
+ set data(value) {
31
+ const oldData = this._data;
32
+ this._data = value;
33
+ // Only re-render if data actually changed and component is initialized
34
+ if (this.isInitialized && !this.isEqual(oldData, value)) {
35
+ this.renderComponent();
36
+ }
37
+ }
38
+ get data() {
39
+ return this._data;
40
+ }
41
+ set state(value) {
42
+ const oldState = this._state;
43
+ this._state = value;
44
+ // Only update state and re-render if it actually changed
45
+ if (this.isInitialized && !this.isEqual(oldState, value)) {
46
+ this.currentState = { ...value };
47
+ this.renderComponent();
48
+ }
49
+ }
50
+ get state() {
51
+ return this._state;
52
+ }
53
+ constructor(reactBridge, adapter, cdr) {
54
+ this.reactBridge = reactBridge;
55
+ this.adapter = adapter;
56
+ this.cdr = cdr;
57
+ this._data = {};
58
+ this._state = {};
59
+ this.utilities = {};
60
+ this.stateChange = new EventEmitter();
61
+ this.componentEvent = new EventEmitter();
62
+ this.refreshData = new EventEmitter();
63
+ this.openEntityRecord = new EventEmitter();
64
+ this.reactRoot = null;
65
+ this.compiledComponent = null;
66
+ this.destroyed$ = new Subject();
67
+ this.currentState = {};
68
+ this.isInitialized = false;
69
+ this.isRendering = false;
70
+ this.pendingRender = false;
71
+ this.hasError = false;
72
+ }
73
+ async ngAfterViewInit() {
74
+ // Trigger change detection to show loading state
75
+ this.cdr.detectChanges();
76
+ await this.initializeComponent();
77
+ }
78
+ ngOnDestroy() {
79
+ this.destroyed$.next();
80
+ this.destroyed$.complete();
81
+ this.cleanup();
82
+ }
83
+ /**
84
+ * Convert SkipComponentStyles to ComponentStyles
85
+ * @param skipStyles - Skip component styles
86
+ * @returns Component styles for React runtime
87
+ */
88
+ convertStyles(skipStyles) {
89
+ // Pass through the full styles object as-is
90
+ // Skip components expect the full structure including colors, typography, etc.
91
+ return skipStyles;
92
+ }
93
+ /**
94
+ * Initialize the React component
95
+ */
96
+ async initializeComponent() {
97
+ try {
98
+ // Ensure React is loaded
99
+ await this.reactBridge.getReactContext();
100
+ // Wait for React to be fully ready (handles first-load delay)
101
+ await this.reactBridge.waitForReactReady();
102
+ // Register component hierarchy
103
+ await this.registerComponentHierarchy();
104
+ // Compile main component
105
+ const result = await this.adapter.compileComponent({
106
+ componentName: this.component.componentName,
107
+ componentCode: this.component.componentCode,
108
+ styles: this.styles
109
+ });
110
+ if (!result.success) {
111
+ throw new Error(result.error?.message || 'Component compilation failed');
112
+ }
113
+ // Get runtime context and execute component factory
114
+ const context = this.adapter.getRuntimeContext();
115
+ this.compiledComponent = result.component.component(context, this.styles);
116
+ this.currentState = { ...this._state };
117
+ // Create React root
118
+ this.reactRoot = this.reactBridge.createRoot(this.container.nativeElement);
119
+ // Initial render
120
+ this.renderComponent();
121
+ this.isInitialized = true;
122
+ // Trigger change detection since we're using OnPush
123
+ this.cdr.detectChanges();
124
+ }
125
+ catch (error) {
126
+ this.hasError = true;
127
+ LogError(`Failed to initialize React component: ${error}`);
128
+ this.componentEvent.emit({
129
+ type: 'error',
130
+ payload: {
131
+ error: error instanceof Error ? error.message : String(error),
132
+ source: 'initialization'
133
+ }
134
+ });
135
+ // Trigger change detection to show error state
136
+ this.cdr.detectChanges();
137
+ }
138
+ }
139
+ /**
140
+ * Register all components in the hierarchy
141
+ */
142
+ async registerComponentHierarchy() {
143
+ // Create the hierarchy registrar with adapter's compiler and registry
144
+ const registrar = new ComponentHierarchyRegistrar(this.adapter.getCompiler(), this.adapter.getRegistry(), this.adapter.getRuntimeContext());
145
+ // Register the entire hierarchy
146
+ const result = await registrar.registerHierarchy(this.component, {
147
+ styles: this.styles, // Skip components use SkipComponentStyles which is a superset
148
+ namespace: 'Global',
149
+ version: 'v1'
150
+ });
151
+ // Check for errors
152
+ if (!result.success) {
153
+ const errorMessages = result.errors.map(e => `${e.componentName}: ${e.error}`);
154
+ throw new Error(`Component registration failed: ${errorMessages.join(', ')}`);
155
+ }
156
+ }
157
+ /**
158
+ * Render the React component
159
+ */
160
+ renderComponent() {
161
+ if (!this.compiledComponent || !this.reactRoot) {
162
+ return;
163
+ }
164
+ // Prevent concurrent renders
165
+ if (this.isRendering) {
166
+ this.pendingRender = true;
167
+ return;
168
+ }
169
+ const context = this.reactBridge.getCurrentContext();
170
+ if (!context) {
171
+ return;
172
+ }
173
+ this.isRendering = true;
174
+ const { React } = context;
175
+ // Get components from resolver
176
+ const components = this.adapter.getResolver().resolveComponents(this.component);
177
+ // Build props - pass styles as-is for Skip components
178
+ const props = buildComponentProps(this._data || {}, this.currentState, this.utilities || {}, this.createCallbacks(), components, this.styles // Skip components expect the full SkipComponentStyles structure
179
+ );
180
+ // Create error boundary
181
+ const ErrorBoundary = createErrorBoundary(React, {
182
+ onError: this.handleReactError.bind(this),
183
+ logErrors: true,
184
+ recovery: 'retry'
185
+ });
186
+ // Create element with error boundary
187
+ const element = React.createElement(ErrorBoundary, null, React.createElement(this.compiledComponent.component, props));
188
+ // Render with timeout protection
189
+ const renderTimeout = setTimeout(() => {
190
+ this.componentEvent.emit({
191
+ type: 'error',
192
+ payload: {
193
+ error: 'Component render timeout - possible infinite loop detected',
194
+ source: 'render'
195
+ }
196
+ });
197
+ }, 5000);
198
+ try {
199
+ this.reactRoot.render(element);
200
+ clearTimeout(renderTimeout);
201
+ // Reset rendering flag after a microtask to allow React to complete
202
+ Promise.resolve().then(() => {
203
+ this.isRendering = false;
204
+ // If there was a pending render request, execute it now
205
+ if (this.pendingRender) {
206
+ this.pendingRender = false;
207
+ this.renderComponent();
208
+ }
209
+ });
210
+ }
211
+ catch (error) {
212
+ clearTimeout(renderTimeout);
213
+ this.isRendering = false;
214
+ this.handleReactError(error);
215
+ }
216
+ }
217
+ /**
218
+ * Create callbacks for the React component
219
+ */
220
+ createCallbacks() {
221
+ return {
222
+ RefreshData: () => {
223
+ this.refreshData.emit();
224
+ },
225
+ OpenEntityRecord: (entityName, key) => {
226
+ // Convert CompositeKey to a simple recordId for the event
227
+ // Try to get the ID field first, otherwise get the first value
228
+ const recordId = key.GetValueByFieldName('ID')?.toString() ||
229
+ key.GetValueByIndex(0)?.toString() ||
230
+ '';
231
+ this.openEntityRecord.emit({ entityName, recordId });
232
+ },
233
+ UpdateUserState: (userState) => {
234
+ // Prevent updates during rendering
235
+ if (this.isRendering) {
236
+ return;
237
+ }
238
+ // Deep comparison to detect actual changes
239
+ const hasChanges = Object.keys(userState).some(key => {
240
+ const currentValue = this.currentState[key];
241
+ const newValue = userState[key];
242
+ return !this.isEqual(currentValue, newValue);
243
+ });
244
+ if (!hasChanges) {
245
+ // No actual changes, skip update to prevent infinite loop
246
+ return;
247
+ }
248
+ this.currentState = {
249
+ ...this.currentState,
250
+ ...userState
251
+ };
252
+ // Emit change for each key in the state update
253
+ Object.keys(userState).forEach(path => {
254
+ this.stateChange.emit({ path, value: userState[path] });
255
+ });
256
+ // Schedule re-render
257
+ this.renderComponent();
258
+ },
259
+ NotifyEvent: (event, data) => {
260
+ this.componentEvent.emit({ type: event, payload: data });
261
+ }
262
+ };
263
+ }
264
+ /**
265
+ * Handle React component errors
266
+ */
267
+ handleReactError(error, errorInfo) {
268
+ LogError(`React component error: ${error?.toString() || 'Unknown error'}`, errorInfo);
269
+ this.componentEvent.emit({
270
+ type: 'error',
271
+ payload: {
272
+ error: error?.toString() || 'Unknown error',
273
+ errorInfo,
274
+ source: 'react'
275
+ }
276
+ });
277
+ }
278
+ /**
279
+ * Clean up resources
280
+ */
281
+ cleanup() {
282
+ // Unmount React root
283
+ if (this.reactRoot) {
284
+ this.reactBridge.unmountRoot(this.reactRoot);
285
+ this.reactRoot = null;
286
+ }
287
+ // Clear references
288
+ this.compiledComponent = null;
289
+ this.currentState = {};
290
+ this.isInitialized = false;
291
+ // Trigger registry cleanup
292
+ this.adapter.getRegistry().cleanup();
293
+ }
294
+ /**
295
+ * Public method to refresh the component
296
+ * @param newData - Optional new data to merge
297
+ */
298
+ refresh(newData) {
299
+ if (newData) {
300
+ // Use the setter to trigger change detection
301
+ this.data = { ...this._data, ...newData };
302
+ }
303
+ else {
304
+ this.renderComponent();
305
+ }
306
+ }
307
+ /**
308
+ * Public method to update state programmatically
309
+ * @param path - State path to update
310
+ * @param value - New value
311
+ */
312
+ updateState(path, value) {
313
+ this.currentState = {
314
+ ...this.currentState,
315
+ [path]: value
316
+ };
317
+ this.stateChange.emit({ path, value });
318
+ this.renderComponent();
319
+ }
320
+ /**
321
+ * Deep equality check that handles null/undefined properly
322
+ */
323
+ isEqual(a, b) {
324
+ // Handle null/undefined cases
325
+ if (a === b)
326
+ return true;
327
+ if (a == null || b == null)
328
+ return false;
329
+ // For objects/arrays, use JSON comparison
330
+ if (typeof a === 'object' && typeof b === 'object') {
331
+ return JSON.stringify(a) === JSON.stringify(b);
332
+ }
333
+ return a === b;
334
+ }
335
+ static { this.ɵfac = function MJReactComponent_Factory(t) { return new (t || MJReactComponent)(i0.ɵɵdirectiveInject(i1.ReactBridgeService), i0.ɵɵdirectiveInject(i2.AngularAdapterService), i0.ɵɵdirectiveInject(i0.ChangeDetectorRef)); }; }
336
+ static { this.ɵcmp = /*@__PURE__*/ i0.ɵɵdefineComponent({ type: MJReactComponent, selectors: [["mj-react-component"]], viewQuery: function MJReactComponent_Query(rf, ctx) { if (rf & 1) {
337
+ i0.ɵɵviewQuery(_c0, 7, ElementRef);
338
+ } if (rf & 2) {
339
+ let _t;
340
+ i0.ɵɵqueryRefresh(_t = i0.ɵɵloadQuery()) && (ctx.container = _t.first);
341
+ } }, inputs: { component: "component", data: "data", state: "state", utilities: "utilities", styles: "styles" }, outputs: { stateChange: "stateChange", componentEvent: "componentEvent", refreshData: "refreshData", openEntityRecord: "openEntityRecord" }, decls: 4, vars: 3, consts: [["container", ""], [1, "react-component-wrapper"], [1, "react-component-container"], [1, "loading-overlay"], [1, "loading-spinner"], [1, "fa-solid", "fa-spinner", "fa-spin"], [1, "loading-text"]], template: function MJReactComponent_Template(rf, ctx) { if (rf & 1) {
342
+ i0.ɵɵelementStart(0, "div", 1);
343
+ i0.ɵɵelement(1, "div", 2, 0);
344
+ i0.ɵɵtemplate(3, MJReactComponent_Conditional_3_Template, 5, 0, "div", 3);
345
+ i0.ɵɵelementEnd();
346
+ } if (rf & 2) {
347
+ i0.ɵɵadvance();
348
+ i0.ɵɵclassProp("loading", !ctx.isInitialized);
349
+ i0.ɵɵadvance(2);
350
+ i0.ɵɵconditional(!ctx.isInitialized && !ctx.hasError ? 3 : -1);
351
+ } }, styles: ["[_nghost-%COMP%] {\n display: block;\n width: 100%;\n height: 100%;\n }\n .react-component-wrapper[_ngcontent-%COMP%] {\n position: relative;\n width: 100%;\n height: 100%;\n }\n .react-component-container[_ngcontent-%COMP%] {\n width: 100%;\n height: 100%;\n transition: opacity 0.3s ease;\n }\n .react-component-container.loading[_ngcontent-%COMP%] {\n opacity: 0;\n }\n .loading-overlay[_ngcontent-%COMP%] {\n position: absolute;\n top: 0;\n left: 0;\n right: 0;\n bottom: 0;\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n background-color: rgba(255, 255, 255, 0.9);\n z-index: 1;\n }\n .loading-spinner[_ngcontent-%COMP%] {\n font-size: 48px;\n color: #5B4FE9;\n margin-bottom: 16px;\n }\n .loading-text[_ngcontent-%COMP%] {\n font-family: -apple-system, BlinkMacSystemFont, \"Inter\", \"Segoe UI\", Roboto, sans-serif;\n font-size: 14px;\n color: #64748B;\n margin-top: 8px;\n }"], changeDetection: 0 }); }
352
+ }
353
+ (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(MJReactComponent, [{
354
+ type: Component,
355
+ args: [{ selector: 'mj-react-component', template: `
356
+ <div class="react-component-wrapper">
357
+ <div #container class="react-component-container" [class.loading]="!isInitialized"></div>
358
+ @if (!isInitialized && !hasError) {
359
+ <div class="loading-overlay">
360
+ <div class="loading-spinner">
361
+ <i class="fa-solid fa-spinner fa-spin"></i>
362
+ </div>
363
+ <div class="loading-text">Loading component...</div>
364
+ </div>
365
+ }
366
+ </div>
367
+ `, changeDetection: ChangeDetectionStrategy.OnPush, styles: ["\n :host {\n display: block;\n width: 100%;\n height: 100%;\n }\n .react-component-wrapper {\n position: relative;\n width: 100%;\n height: 100%;\n }\n .react-component-container {\n width: 100%;\n height: 100%;\n transition: opacity 0.3s ease;\n }\n .react-component-container.loading {\n opacity: 0;\n }\n .loading-overlay {\n position: absolute;\n top: 0;\n left: 0;\n right: 0;\n bottom: 0;\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n background-color: rgba(255, 255, 255, 0.9);\n z-index: 1;\n }\n .loading-spinner {\n font-size: 48px;\n color: #5B4FE9;\n margin-bottom: 16px;\n }\n .loading-text {\n font-family: -apple-system, BlinkMacSystemFont, \"Inter\", \"Segoe UI\", Roboto, sans-serif;\n font-size: 14px;\n color: #64748B;\n margin-top: 8px;\n }\n "] }]
368
+ }], () => [{ type: i1.ReactBridgeService }, { type: i2.AngularAdapterService }, { type: i0.ChangeDetectorRef }], { component: [{
369
+ type: Input
370
+ }], data: [{
371
+ type: Input
372
+ }], state: [{
373
+ type: Input
374
+ }], utilities: [{
375
+ type: Input
376
+ }], styles: [{
377
+ type: Input
378
+ }], stateChange: [{
379
+ type: Output
380
+ }], componentEvent: [{
381
+ type: Output
382
+ }], refreshData: [{
383
+ type: Output
384
+ }], openEntityRecord: [{
385
+ type: Output
386
+ }], container: [{
387
+ type: ViewChild,
388
+ args: ['container', { read: ElementRef, static: true }]
389
+ }] }); })();
390
+ (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassDebugInfo(MJReactComponent, { className: "MJReactComponent", filePath: "lib/components/mj-react-component.component.ts", lineNumber: 116 }); })();
391
+ //# sourceMappingURL=mj-react-component.component.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mj-react-component.component.js","sourceRoot":"","sources":["../../../src/lib/components/mj-react-component.component.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * @fileoverview Angular component that hosts React components with proper memory management.\n * Provides a bridge between Angular and React ecosystems in MemberJunction applications.\n * @module @memberjunction/ng-react\n */\n\nimport {\n Component,\n Input,\n Output,\n EventEmitter,\n ViewChild,\n ElementRef,\n AfterViewInit,\n OnDestroy,\n ChangeDetectionStrategy,\n ChangeDetectorRef\n} from '@angular/core';\nimport { Subject, takeUntil } from 'rxjs';\nimport { SkipComponentRootSpec, SkipComponentCallbacks, SkipComponentStyles } from '@memberjunction/skip-types';\nimport { ReactBridgeService } from '../services/react-bridge.service';\nimport { AngularAdapterService } from '../services/angular-adapter.service';\nimport { \n buildComponentProps, \n ComponentCallbacks,\n ComponentError,\n createErrorBoundary,\n ComponentStyles,\n ComponentHierarchyRegistrar,\n HierarchyRegistrationResult\n} from '@memberjunction/react-runtime';\nimport { LogError, CompositeKey } from '@memberjunction/core';\n\n/**\n * Event emitted by React components\n */\nexport interface ReactComponentEvent {\n type: string;\n payload: any;\n}\n\n/**\n * State change event emitted when component state updates\n */\nexport interface StateChangeEvent {\n path: string;\n value: any;\n}\n\n/**\n * Angular component that hosts React components with proper memory management.\n * This component provides a bridge between Angular and React, allowing React components\n * to be used seamlessly within Angular applications.\n */\n@Component({\n selector: 'mj-react-component',\n template: `\n <div class=\"react-component-wrapper\">\n <div #container class=\"react-component-container\" [class.loading]=\"!isInitialized\"></div>\n @if (!isInitialized && !hasError) {\n <div class=\"loading-overlay\">\n <div class=\"loading-spinner\">\n <i class=\"fa-solid fa-spinner fa-spin\"></i>\n </div>\n <div class=\"loading-text\">Loading component...</div>\n </div>\n }\n </div>\n `,\n styles: [`\n :host {\n display: block;\n width: 100%;\n height: 100%;\n }\n .react-component-wrapper {\n position: relative;\n width: 100%;\n height: 100%;\n }\n .react-component-container {\n width: 100%;\n height: 100%;\n transition: opacity 0.3s ease;\n }\n .react-component-container.loading {\n opacity: 0;\n }\n .loading-overlay {\n position: absolute;\n top: 0;\n left: 0;\n right: 0;\n bottom: 0;\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n background-color: rgba(255, 255, 255, 0.9);\n z-index: 1;\n }\n .loading-spinner {\n font-size: 48px;\n color: #5B4FE9;\n margin-bottom: 16px;\n }\n .loading-text {\n font-family: -apple-system, BlinkMacSystemFont, \"Inter\", \"Segoe UI\", Roboto, sans-serif;\n font-size: 14px;\n color: #64748B;\n margin-top: 8px;\n }\n `],\n changeDetection: ChangeDetectionStrategy.OnPush\n})\nexport class MJReactComponent implements AfterViewInit, OnDestroy {\n @Input() component!: SkipComponentRootSpec;\n \n private _data: any = {};\n @Input() \n set data(value: any) {\n const oldData = this._data;\n this._data = value;\n \n // Only re-render if data actually changed and component is initialized\n if (this.isInitialized && !this.isEqual(oldData, value)) {\n this.renderComponent();\n }\n }\n get data(): any {\n return this._data;\n }\n \n private _state: any = {};\n @Input() \n set state(value: any) {\n const oldState = this._state;\n this._state = value;\n \n // Only update state and re-render if it actually changed\n if (this.isInitialized && !this.isEqual(oldState, value)) {\n this.currentState = { ...value };\n this.renderComponent();\n }\n }\n get state(): any {\n return this._state;\n }\n \n @Input() utilities: any = {};\n @Input() styles?: Partial<SkipComponentStyles>;\n \n @Output() stateChange = new EventEmitter<StateChangeEvent>();\n @Output() componentEvent = new EventEmitter<ReactComponentEvent>();\n @Output() refreshData = new EventEmitter<void>();\n @Output() openEntityRecord = new EventEmitter<{ entityName: string; recordId: string }>();\n \n @ViewChild('container', { read: ElementRef, static: true }) container!: ElementRef<HTMLDivElement>;\n \n private reactRoot: any = null;\n private compiledComponent: any = null;\n private destroyed$ = new Subject<void>();\n private currentState: any = {};\n isInitialized = false;\n private isRendering = false;\n private pendingRender = false;\n hasError = false;\n\n constructor(\n private reactBridge: ReactBridgeService,\n private adapter: AngularAdapterService,\n private cdr: ChangeDetectorRef\n ) {}\n\n async ngAfterViewInit() {\n // Trigger change detection to show loading state\n this.cdr.detectChanges();\n await this.initializeComponent();\n }\n\n ngOnDestroy() {\n this.destroyed$.next();\n this.destroyed$.complete();\n this.cleanup();\n }\n\n /**\n * Convert SkipComponentStyles to ComponentStyles\n * @param skipStyles - Skip component styles\n * @returns Component styles for React runtime\n */\n private convertStyles(skipStyles?: Partial<SkipComponentStyles>): any {\n // Pass through the full styles object as-is\n // Skip components expect the full structure including colors, typography, etc.\n return skipStyles;\n }\n\n /**\n * Initialize the React component\n */\n private async initializeComponent() {\n try {\n // Ensure React is loaded\n await this.reactBridge.getReactContext();\n \n // Wait for React to be fully ready (handles first-load delay)\n await this.reactBridge.waitForReactReady();\n \n // Register component hierarchy\n await this.registerComponentHierarchy();\n \n // Compile main component\n const result = await this.adapter.compileComponent({\n componentName: this.component.componentName,\n componentCode: this.component.componentCode,\n styles: this.styles\n });\n\n if (!result.success) {\n throw new Error(result.error?.message || 'Component compilation failed');\n }\n\n // Get runtime context and execute component factory\n const context = this.adapter.getRuntimeContext();\n this.compiledComponent = result.component!.component(context, this.styles);\n this.currentState = { ...this._state };\n \n // Create React root\n this.reactRoot = this.reactBridge.createRoot(this.container.nativeElement);\n \n // Initial render\n this.renderComponent();\n this.isInitialized = true;\n \n // Trigger change detection since we're using OnPush\n this.cdr.detectChanges();\n \n } catch (error) {\n this.hasError = true;\n LogError(`Failed to initialize React component: ${error}`);\n this.componentEvent.emit({\n type: 'error',\n payload: {\n error: error instanceof Error ? error.message : String(error),\n source: 'initialization'\n }\n });\n // Trigger change detection to show error state\n this.cdr.detectChanges();\n }\n }\n\n /**\n * Register all components in the hierarchy\n */\n private async registerComponentHierarchy() {\n // Create the hierarchy registrar with adapter's compiler and registry\n const registrar = new ComponentHierarchyRegistrar(\n this.adapter.getCompiler(),\n this.adapter.getRegistry(),\n this.adapter.getRuntimeContext()\n );\n \n // Register the entire hierarchy\n const result: HierarchyRegistrationResult = await registrar.registerHierarchy(\n this.component,\n {\n styles: this.styles as any, // Skip components use SkipComponentStyles which is a superset\n namespace: 'Global',\n version: 'v1'\n }\n );\n \n // Check for errors\n if (!result.success) {\n const errorMessages = result.errors.map(e => \n `${e.componentName}: ${e.error}`\n );\n throw new Error(`Component registration failed: ${errorMessages.join(', ')}`);\n }\n }\n\n /**\n * Render the React component\n */\n private renderComponent() {\n if (!this.compiledComponent || !this.reactRoot) {\n return;\n }\n\n // Prevent concurrent renders\n if (this.isRendering) {\n this.pendingRender = true;\n return;\n }\n\n const context = this.reactBridge.getCurrentContext();\n if (!context) {\n return;\n }\n\n this.isRendering = true;\n const { React } = context;\n \n // Get components from resolver\n const components = this.adapter.getResolver().resolveComponents(this.component);\n \n // Build props - pass styles as-is for Skip components\n const props = buildComponentProps(\n this._data || {},\n this.currentState,\n this.utilities || {},\n this.createCallbacks(),\n components,\n this.styles as any // Skip components expect the full SkipComponentStyles structure\n );\n\n // Create error boundary\n const ErrorBoundary = createErrorBoundary(React, {\n onError: this.handleReactError.bind(this),\n logErrors: true,\n recovery: 'retry'\n });\n\n // Create element with error boundary\n const element = React.createElement(\n ErrorBoundary,\n null,\n React.createElement(this.compiledComponent.component, props)\n );\n\n // Render with timeout protection\n const renderTimeout = setTimeout(() => {\n this.componentEvent.emit({\n type: 'error',\n payload: {\n error: 'Component render timeout - possible infinite loop detected',\n source: 'render'\n }\n });\n }, 5000);\n\n try {\n this.reactRoot.render(element);\n clearTimeout(renderTimeout);\n \n // Reset rendering flag after a microtask to allow React to complete\n Promise.resolve().then(() => {\n this.isRendering = false;\n \n // If there was a pending render request, execute it now\n if (this.pendingRender) {\n this.pendingRender = false;\n this.renderComponent();\n }\n });\n } catch (error) {\n clearTimeout(renderTimeout);\n this.isRendering = false;\n this.handleReactError(error);\n }\n }\n\n /**\n * Create callbacks for the React component\n */\n private createCallbacks(): ComponentCallbacks {\n return {\n RefreshData: () => {\n this.refreshData.emit();\n },\n OpenEntityRecord: (entityName: string, key: CompositeKey) => {\n // Convert CompositeKey to a simple recordId for the event\n // Try to get the ID field first, otherwise get the first value\n const recordId = key.GetValueByFieldName('ID')?.toString() || \n key.GetValueByIndex(0)?.toString() || \n '';\n this.openEntityRecord.emit({ entityName, recordId });\n },\n UpdateUserState: (userState: any) => {\n // Prevent updates during rendering\n if (this.isRendering) {\n return;\n }\n \n // Deep comparison to detect actual changes\n const hasChanges = Object.keys(userState).some(key => {\n const currentValue = this.currentState[key];\n const newValue = userState[key];\n return !this.isEqual(currentValue, newValue);\n });\n \n if (!hasChanges) {\n // No actual changes, skip update to prevent infinite loop\n return;\n }\n \n this.currentState = {\n ...this.currentState,\n ...userState\n };\n \n // Emit change for each key in the state update\n Object.keys(userState).forEach(path => {\n this.stateChange.emit({ path, value: userState[path] });\n });\n \n // Schedule re-render\n this.renderComponent();\n },\n NotifyEvent: (event: string, data: any) => {\n this.componentEvent.emit({ type: event, payload: data });\n }\n };\n }\n\n /**\n * Handle React component errors\n */\n private handleReactError(error: any, errorInfo?: any) {\n LogError(`React component error: ${error?.toString() || 'Unknown error'}`, errorInfo);\n this.componentEvent.emit({\n type: 'error',\n payload: {\n error: error?.toString() || 'Unknown error',\n errorInfo,\n source: 'react'\n }\n });\n }\n\n /**\n * Clean up resources\n */\n private cleanup() {\n // Unmount React root\n if (this.reactRoot) {\n this.reactBridge.unmountRoot(this.reactRoot);\n this.reactRoot = null;\n }\n\n // Clear references\n this.compiledComponent = null;\n this.currentState = {};\n this.isInitialized = false;\n\n // Trigger registry cleanup\n this.adapter.getRegistry().cleanup();\n }\n\n /**\n * Public method to refresh the component\n * @param newData - Optional new data to merge\n */\n refresh(newData?: any) {\n if (newData) {\n // Use the setter to trigger change detection\n this.data = { ...this._data, ...newData };\n } else {\n this.renderComponent();\n }\n }\n\n /**\n * Public method to update state programmatically\n * @param path - State path to update\n * @param value - New value\n */\n updateState(path: string, value: any) {\n this.currentState = {\n ...this.currentState,\n [path]: value\n };\n this.stateChange.emit({ path, value });\n this.renderComponent();\n }\n\n /**\n * Deep equality check that handles null/undefined properly\n */\n private isEqual(a: any, b: any): boolean {\n // Handle null/undefined cases\n if (a === b) return true;\n if (a == null || b == null) return false;\n \n // For objects/arrays, use JSON comparison\n if (typeof a === 'object' && typeof b === 'object') {\n return JSON.stringify(a) === JSON.stringify(b);\n }\n \n return a === b;\n }\n}"]}
@@ -0,0 +1,10 @@
1
+ /**
2
+ * @fileoverview Default styles for React components in MemberJunction.
3
+ * These styles provide a consistent design system for React components.
4
+ * @module @memberjunction/ng-react
5
+ */
6
+ import { SkipComponentStyles } from '@memberjunction/skip-types';
7
+ /**
8
+ * Default styles that match the Skip design system
9
+ */
10
+ export declare const DEFAULT_STYLES: SkipComponentStyles;
@@ -0,0 +1,106 @@
1
+ /**
2
+ * @fileoverview Default styles for React components in MemberJunction.
3
+ * These styles provide a consistent design system for React components.
4
+ * @module @memberjunction/ng-react
5
+ */
6
+ /**
7
+ * Default styles that match the Skip design system
8
+ */
9
+ export const DEFAULT_STYLES = {
10
+ colors: {
11
+ // Primary colors - modern purple/blue gradient feel
12
+ primary: '#5B4FE9',
13
+ primaryHover: '#4940D4',
14
+ primaryLight: '#E8E6FF',
15
+ // Secondary colors - sophisticated gray
16
+ secondary: '#64748B',
17
+ secondaryHover: '#475569',
18
+ // Status colors
19
+ success: '#10B981',
20
+ successLight: '#D1FAE5',
21
+ warning: '#F59E0B',
22
+ warningLight: '#FEF3C7',
23
+ error: '#EF4444',
24
+ errorLight: '#FEE2E2',
25
+ info: '#3B82F6',
26
+ infoLight: '#DBEAFE',
27
+ // Base colors
28
+ background: '#FFFFFF',
29
+ surface: '#F8FAFC',
30
+ surfaceHover: '#F1F5F9',
31
+ // Text colors with better contrast
32
+ text: '#1E293B',
33
+ textSecondary: '#64748B',
34
+ textTertiary: '#94A3B8',
35
+ textInverse: '#FFFFFF',
36
+ // Border colors
37
+ border: '#E2E8F0',
38
+ borderLight: '#F1F5F9',
39
+ borderFocus: '#5B4FE9',
40
+ // Shadows (as color strings for easy use)
41
+ shadow: 'rgba(0, 0, 0, 0.05)',
42
+ shadowMedium: 'rgba(0, 0, 0, 0.1)',
43
+ shadowLarge: 'rgba(0, 0, 0, 0.15)',
44
+ },
45
+ spacing: {
46
+ xs: '4px',
47
+ sm: '8px',
48
+ md: '16px',
49
+ lg: '24px',
50
+ xl: '32px',
51
+ xxl: '48px',
52
+ xxxl: '64px',
53
+ },
54
+ typography: {
55
+ fontFamily: '-apple-system, BlinkMacSystemFont, "Inter", "Segoe UI", Roboto, sans-serif',
56
+ fontSize: {
57
+ xs: '11px',
58
+ sm: '12px',
59
+ md: '14px',
60
+ lg: '16px',
61
+ xl: '20px',
62
+ xxl: '24px',
63
+ xxxl: '32px',
64
+ },
65
+ fontWeight: {
66
+ light: '300',
67
+ regular: '400',
68
+ medium: '500',
69
+ semibold: '600',
70
+ bold: '700',
71
+ },
72
+ lineHeight: {
73
+ tight: '1.25',
74
+ normal: '1.5',
75
+ relaxed: '1.75',
76
+ },
77
+ },
78
+ borders: {
79
+ radius: {
80
+ sm: '6px',
81
+ md: '8px',
82
+ lg: '12px',
83
+ xl: '16px',
84
+ full: '9999px',
85
+ },
86
+ width: {
87
+ thin: '1px',
88
+ medium: '2px',
89
+ thick: '3px',
90
+ },
91
+ },
92
+ shadows: {
93
+ sm: '0 1px 2px 0 rgba(0, 0, 0, 0.05)',
94
+ md: '0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06)',
95
+ lg: '0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05)',
96
+ xl: '0 20px 25px -5px rgba(0, 0, 0, 0.1), 0 10px 10px -5px rgba(0, 0, 0, 0.04)',
97
+ inner: 'inset 0 2px 4px 0 rgba(0, 0, 0, 0.06)',
98
+ },
99
+ transitions: {
100
+ fast: '150ms ease-in-out',
101
+ normal: '250ms ease-in-out',
102
+ slow: '350ms ease-in-out',
103
+ },
104
+ overflow: 'auto'
105
+ };
106
+ //# sourceMappingURL=default-styles.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"default-styles.js","sourceRoot":"","sources":["../../src/lib/default-styles.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAIH;;GAEG;AACH,MAAM,CAAC,MAAM,cAAc,GAAwB;IACjD,MAAM,EAAE;QACN,oDAAoD;QACpD,OAAO,EAAE,SAAS;QAClB,YAAY,EAAE,SAAS;QACvB,YAAY,EAAE,SAAS;QAEvB,wCAAwC;QACxC,SAAS,EAAE,SAAS;QACpB,cAAc,EAAE,SAAS;QAEzB,gBAAgB;QAChB,OAAO,EAAE,SAAS;QAClB,YAAY,EAAE,SAAS;QACvB,OAAO,EAAE,SAAS;QAClB,YAAY,EAAE,SAAS;QACvB,KAAK,EAAE,SAAS;QAChB,UAAU,EAAE,SAAS;QACrB,IAAI,EAAE,SAAS;QACf,SAAS,EAAE,SAAS;QAEpB,cAAc;QACd,UAAU,EAAE,SAAS;QACrB,OAAO,EAAE,SAAS;QAClB,YAAY,EAAE,SAAS;QAEvB,mCAAmC;QACnC,IAAI,EAAE,SAAS;QACf,aAAa,EAAE,SAAS;QACxB,YAAY,EAAE,SAAS;QACvB,WAAW,EAAE,SAAS;QAEtB,gBAAgB;QAChB,MAAM,EAAE,SAAS;QACjB,WAAW,EAAE,SAAS;QACtB,WAAW,EAAE,SAAS;QAEtB,0CAA0C;QAC1C,MAAM,EAAE,qBAAqB;QAC7B,YAAY,EAAE,oBAAoB;QAClC,WAAW,EAAE,qBAAqB;KACnC;IACD,OAAO,EAAE;QACP,EAAE,EAAE,KAAK;QACT,EAAE,EAAE,KAAK;QACT,EAAE,EAAE,MAAM;QACV,EAAE,EAAE,MAAM;QACV,EAAE,EAAE,MAAM;QACV,GAAG,EAAE,MAAM;QACX,IAAI,EAAE,MAAM;KACb;IACD,UAAU,EAAE;QACV,UAAU,EAAE,4EAA4E;QACxF,QAAQ,EAAE;YACR,EAAE,EAAE,MAAM;YACV,EAAE,EAAE,MAAM;YACV,EAAE,EAAE,MAAM;YACV,EAAE,EAAE,MAAM;YACV,EAAE,EAAE,MAAM;YACV,GAAG,EAAE,MAAM;YACX,IAAI,EAAE,MAAM;SACb;QACD,UAAU,EAAE;YACV,KAAK,EAAE,KAAK;YACZ,OAAO,EAAE,KAAK;YACd,MAAM,EAAE,KAAK;YACb,QAAQ,EAAE,KAAK;YACf,IAAI,EAAE,KAAK;SACZ;QACD,UAAU,EAAE;YACV,KAAK,EAAE,MAAM;YACb,MAAM,EAAE,KAAK;YACb,OAAO,EAAE,MAAM;SAChB;KACF;IACD,OAAO,EAAE;QACP,MAAM,EAAE;YACN,EAAE,EAAE,KAAK;YACT,EAAE,EAAE,KAAK;YACT,EAAE,EAAE,MAAM;YACV,EAAE,EAAE,MAAM;YACV,IAAI,EAAE,QAAQ;SACf;QACD,KAAK,EAAE;YACL,IAAI,EAAE,KAAK;YACX,MAAM,EAAE,KAAK;YACb,KAAK,EAAE,KAAK;SACb;KACF;IACD,OAAO,EAAE;QACP,EAAE,EAAE,iCAAiC;QACrC,EAAE,EAAE,uEAAuE;QAC3E,EAAE,EAAE,yEAAyE;QAC7E,EAAE,EAAE,2EAA2E;QAC/E,KAAK,EAAE,uCAAuC;KAC/C;IACD,WAAW,EAAE;QACX,IAAI,EAAE,mBAAmB;QACzB,MAAM,EAAE,mBAAmB;QAC3B,IAAI,EAAE,mBAAmB;KAC1B;IACD,QAAQ,EAAE,MAAM;CACjB,CAAC","sourcesContent":["/**\n * @fileoverview Default styles for React components in MemberJunction.\n * These styles provide a consistent design system for React components.\n * @module @memberjunction/ng-react\n */\n\nimport { SkipComponentStyles } from '@memberjunction/skip-types';\n\n/**\n * Default styles that match the Skip design system\n */\nexport const DEFAULT_STYLES: SkipComponentStyles = {\n colors: {\n // Primary colors - modern purple/blue gradient feel\n primary: '#5B4FE9',\n primaryHover: '#4940D4',\n primaryLight: '#E8E6FF',\n \n // Secondary colors - sophisticated gray\n secondary: '#64748B',\n secondaryHover: '#475569',\n \n // Status colors\n success: '#10B981',\n successLight: '#D1FAE5',\n warning: '#F59E0B',\n warningLight: '#FEF3C7',\n error: '#EF4444',\n errorLight: '#FEE2E2',\n info: '#3B82F6',\n infoLight: '#DBEAFE',\n \n // Base colors\n background: '#FFFFFF',\n surface: '#F8FAFC',\n surfaceHover: '#F1F5F9',\n \n // Text colors with better contrast\n text: '#1E293B',\n textSecondary: '#64748B',\n textTertiary: '#94A3B8',\n textInverse: '#FFFFFF',\n \n // Border colors\n border: '#E2E8F0',\n borderLight: '#F1F5F9',\n borderFocus: '#5B4FE9',\n \n // Shadows (as color strings for easy use)\n shadow: 'rgba(0, 0, 0, 0.05)',\n shadowMedium: 'rgba(0, 0, 0, 0.1)',\n shadowLarge: 'rgba(0, 0, 0, 0.15)',\n },\n spacing: {\n xs: '4px',\n sm: '8px',\n md: '16px',\n lg: '24px',\n xl: '32px',\n xxl: '48px',\n xxxl: '64px',\n },\n typography: {\n fontFamily: '-apple-system, BlinkMacSystemFont, \"Inter\", \"Segoe UI\", Roboto, sans-serif',\n fontSize: {\n xs: '11px',\n sm: '12px',\n md: '14px',\n lg: '16px',\n xl: '20px',\n xxl: '24px',\n xxxl: '32px',\n },\n fontWeight: {\n light: '300',\n regular: '400',\n medium: '500',\n semibold: '600',\n bold: '700',\n },\n lineHeight: {\n tight: '1.25',\n normal: '1.5',\n relaxed: '1.75',\n },\n },\n borders: {\n radius: {\n sm: '6px',\n md: '8px',\n lg: '12px',\n xl: '16px',\n full: '9999px',\n },\n width: {\n thin: '1px',\n medium: '2px',\n thick: '3px',\n },\n },\n shadows: {\n sm: '0 1px 2px 0 rgba(0, 0, 0, 0.05)',\n md: '0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06)',\n lg: '0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05)',\n xl: '0 20px 25px -5px rgba(0, 0, 0, 0.1), 0 10px 10px -5px rgba(0, 0, 0, 0.04)',\n inner: 'inset 0 2px 4px 0 rgba(0, 0, 0, 0.06)',\n },\n transitions: {\n fast: '150ms ease-in-out',\n normal: '250ms ease-in-out',\n slow: '350ms ease-in-out',\n },\n overflow: 'auto'\n};"]}
@@ -0,0 +1,22 @@
1
+ import * as i0 from "@angular/core";
2
+ import * as i1 from "./components/mj-react-component.component";
3
+ import * as i2 from "@angular/common";
4
+ /**
5
+ * Angular module that provides React component hosting capabilities.
6
+ * Import this module to use React components within your Angular application.
7
+ *
8
+ * @example
9
+ * ```typescript
10
+ * import { MJReactModule } from '@memberjunction/ng-react';
11
+ *
12
+ * @NgModule({
13
+ * imports: [MJReactModule]
14
+ * })
15
+ * export class MyModule {}
16
+ * ```
17
+ */
18
+ export declare class MJReactModule {
19
+ static ɵfac: i0.ɵɵFactoryDeclaration<MJReactModule, never>;
20
+ static ɵmod: i0.ɵɵNgModuleDeclaration<MJReactModule, [typeof i1.MJReactComponent], [typeof i2.CommonModule], [typeof i1.MJReactComponent]>;
21
+ static ɵinj: i0.ɵɵInjectorDeclaration<MJReactModule>;
22
+ }
@@ -0,0 +1,58 @@
1
+ /**
2
+ * @fileoverview Angular module for React component integration in MemberJunction.
3
+ * Provides components and services for hosting React components within Angular applications.
4
+ * @module @memberjunction/ng-react
5
+ */
6
+ import { NgModule } from '@angular/core';
7
+ import { CommonModule } from '@angular/common';
8
+ // Components
9
+ import { MJReactComponent } from './components/mj-react-component.component';
10
+ // Services
11
+ import { ScriptLoaderService } from './services/script-loader.service';
12
+ import { ReactBridgeService } from './services/react-bridge.service';
13
+ import { AngularAdapterService } from './services/angular-adapter.service';
14
+ import * as i0 from "@angular/core";
15
+ /**
16
+ * Angular module that provides React component hosting capabilities.
17
+ * Import this module to use React components within your Angular application.
18
+ *
19
+ * @example
20
+ * ```typescript
21
+ * import { MJReactModule } from '@memberjunction/ng-react';
22
+ *
23
+ * @NgModule({
24
+ * imports: [MJReactModule]
25
+ * })
26
+ * export class MyModule {}
27
+ * ```
28
+ */
29
+ export class MJReactModule {
30
+ static { this.ɵfac = function MJReactModule_Factory(t) { return new (t || MJReactModule)(); }; }
31
+ static { this.ɵmod = /*@__PURE__*/ i0.ɵɵdefineNgModule({ type: MJReactModule }); }
32
+ static { this.ɵinj = /*@__PURE__*/ i0.ɵɵdefineInjector({ providers: [
33
+ ScriptLoaderService,
34
+ ReactBridgeService,
35
+ AngularAdapterService
36
+ ], imports: [CommonModule] }); }
37
+ }
38
+ (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(MJReactModule, [{
39
+ type: NgModule,
40
+ args: [{
41
+ declarations: [
42
+ MJReactComponent
43
+ ],
44
+ imports: [
45
+ CommonModule
46
+ ],
47
+ providers: [
48
+ ScriptLoaderService,
49
+ ReactBridgeService,
50
+ AngularAdapterService
51
+ ],
52
+ exports: [
53
+ MJReactComponent
54
+ ]
55
+ }]
56
+ }], null, null); })();
57
+ (function () { (typeof ngJitMode === "undefined" || ngJitMode) && i0.ɵɵsetNgModuleScope(MJReactModule, { declarations: [MJReactComponent], imports: [CommonModule], exports: [MJReactComponent] }); })();
58
+ //# sourceMappingURL=module.js.map