@bbq-chat/widgets-angular 1.0.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,316 @@
1
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
2
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
3
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
4
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
5
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
6
+ };
7
+ var __metadata = (this && this.__metadata) || function (k, v) {
8
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
9
+ };
10
+ import { Component, Input, Output, EventEmitter, ElementRef, ViewChild, inject, Injector, createComponent, EnvironmentInjector, } from '@angular/core';
11
+ import { CommonModule } from '@angular/common';
12
+ import { SsrWidgetRenderer, WidgetEventManager, } from '@bbq-chat/widgets';
13
+ import { WidgetRegistryService } from './widget-registry.service';
14
+ import { isHtmlRenderer, isComponentRenderer, isTemplateRenderer, } from './custom-widget-renderer.types';
15
+ /**
16
+ * Angular component for rendering chat widgets
17
+ *
18
+ * This component handles rendering of chat widgets using the BbQ ChatWidgets library.
19
+ * It manages widget lifecycle, event handling, and cleanup.
20
+ *
21
+ * Supports three types of custom widget renderers:
22
+ * 1. HTML function renderers (return HTML strings)
23
+ * 2. Angular Component renderers (render as dynamic components)
24
+ * 3. Angular TemplateRef renderers (render as embedded views)
25
+ *
26
+ * @example
27
+ * ```typescript
28
+ * <bbq-widget-renderer
29
+ * [widgets]="messageWidgets"
30
+ * (widgetAction)="handleWidgetAction($event)">
31
+ * </bbq-widget-renderer>
32
+ * ```
33
+ */
34
+ let WidgetRendererComponent = class WidgetRendererComponent {
35
+ constructor() {
36
+ /**
37
+ * Emits when a widget action is triggered
38
+ */
39
+ this.widgetAction = new EventEmitter();
40
+ this.widgetItems = [];
41
+ this.renderer = new SsrWidgetRenderer();
42
+ this.isViewInitialized = false;
43
+ this.widgetRegistry = inject(WidgetRegistryService);
44
+ this.injector = inject(Injector);
45
+ this.environmentInjector = inject(EnvironmentInjector);
46
+ this.dynamicComponents = [];
47
+ this.dynamicViews = [];
48
+ }
49
+ ngOnInit() {
50
+ this.updateWidgetHtml();
51
+ }
52
+ ngOnChanges(changes) {
53
+ if (changes['widgets']) {
54
+ this.updateWidgetHtml();
55
+ }
56
+ }
57
+ ngAfterViewInit() {
58
+ this.isViewInitialized = true;
59
+ this.setupEventHandlers();
60
+ // Render dynamic components/templates after view init
61
+ this.renderDynamicWidgets();
62
+ }
63
+ ngOnDestroy() {
64
+ this.cleanup();
65
+ }
66
+ /**
67
+ * Base implementation for updating the rendered HTML for the current widgets.
68
+ *
69
+ * Subclasses may override this method to customize how widgets are rendered
70
+ * (for example, to inject additional markup or perform preprocessing).
71
+ *
72
+ * Since this is the base implementation, overriding implementations are not
73
+ * required to call `super.updateWidgetHtml()`.
74
+ */
75
+ updateWidgetHtml() {
76
+ if (!this.widgets || this.widgets.length === 0) {
77
+ this.widgetItems = [];
78
+ return;
79
+ }
80
+ this.widgetItems = this.widgets.map((widget, index) => {
81
+ const customRenderer = this.widgetRegistry.getRenderer(widget.type);
82
+ // Check template renderer first (most specific)
83
+ if (customRenderer && isTemplateRenderer(customRenderer)) {
84
+ return {
85
+ index,
86
+ widget,
87
+ isHtml: false,
88
+ };
89
+ }
90
+ // Check component renderer second
91
+ if (customRenderer && isComponentRenderer(customRenderer)) {
92
+ return {
93
+ index,
94
+ widget,
95
+ isHtml: false,
96
+ };
97
+ }
98
+ // Check HTML function renderer last (most general, matches any function)
99
+ if (customRenderer && isHtmlRenderer(customRenderer)) {
100
+ return {
101
+ index,
102
+ widget,
103
+ isHtml: true,
104
+ html: customRenderer(widget),
105
+ };
106
+ }
107
+ // Default: render using the BbQ library renderer
108
+ return {
109
+ index,
110
+ widget,
111
+ isHtml: true,
112
+ html: this.renderer.renderWidget(widget),
113
+ };
114
+ });
115
+ // After view updates, reinitialize widgets only if view is already initialized
116
+ if (this.isViewInitialized) {
117
+ setTimeout(() => {
118
+ this.setupEventHandlers();
119
+ this.renderDynamicWidgets();
120
+ }, 0);
121
+ }
122
+ }
123
+ /**
124
+ * Render dynamic components and templates for custom widgets
125
+ */
126
+ renderDynamicWidgets() {
127
+ if (!this.containerRef?.nativeElement)
128
+ return;
129
+ // Use microtask to ensure Angular has completed change detection
130
+ Promise.resolve().then(() => {
131
+ if (!this.containerRef?.nativeElement)
132
+ return;
133
+ // Clean up existing dynamic components and views
134
+ this.cleanupDynamicWidgets();
135
+ const container = this.containerRef.nativeElement;
136
+ // Query all widget divs without the data-rendered filter
137
+ const dynamicWidgetDivs = Array.from(container.querySelectorAll('.bbq-widget'));
138
+ let dynamicIndex = 0;
139
+ this.widgetItems.forEach((item) => {
140
+ if (!item.isHtml) {
141
+ const customRenderer = this.widgetRegistry.getRenderer(item.widget.type);
142
+ if (!customRenderer)
143
+ return;
144
+ const targetDiv = dynamicWidgetDivs[dynamicIndex];
145
+ if (!targetDiv)
146
+ return;
147
+ // Clear the div content before rendering
148
+ targetDiv.innerHTML = '';
149
+ if (isComponentRenderer(customRenderer)) {
150
+ this.renderComponent(customRenderer, item.widget, targetDiv);
151
+ }
152
+ else if (isTemplateRenderer(customRenderer)) {
153
+ this.renderTemplate(customRenderer, item.widget, targetDiv);
154
+ }
155
+ dynamicIndex++;
156
+ }
157
+ });
158
+ });
159
+ }
160
+ /**
161
+ * Render an Angular component for a custom widget
162
+ *
163
+ * Note: This method safely assigns properties to component instances
164
+ * by checking for property existence at runtime. This approach is necessary
165
+ * because we cannot statically verify that all components implement
166
+ * the CustomWidgetComponent interface.
167
+ */
168
+ renderComponent(componentType, widget, targetElement) {
169
+ // Create the component using Angular's createComponent API
170
+ const componentRef = createComponent(componentType, {
171
+ environmentInjector: this.environmentInjector,
172
+ elementInjector: this.injector,
173
+ });
174
+ // Safely set component inputs if they exist
175
+ const instance = componentRef.instance;
176
+ if (instance && typeof instance === 'object') {
177
+ // Set widget property if it exists in the prototype chain
178
+ if ('widget' in instance) {
179
+ instance.widget = widget;
180
+ }
181
+ // Set widgetAction callback if it exists in the prototype chain
182
+ if ('widgetAction' in instance) {
183
+ instance.widgetAction = (actionName, payload) => {
184
+ this.widgetAction.emit({ actionName, payload });
185
+ };
186
+ }
187
+ }
188
+ // Attach the component's host view to the target element
189
+ targetElement.appendChild(componentRef.location.nativeElement);
190
+ // Store reference for cleanup
191
+ this.dynamicComponents.push(componentRef);
192
+ // Trigger change detection (use optional chaining for safety)
193
+ componentRef.changeDetectorRef?.detectChanges();
194
+ }
195
+ /**
196
+ * Render an Angular template for a custom widget
197
+ */
198
+ renderTemplate(templateRef, widget, targetElement) {
199
+ const context = {
200
+ $implicit: widget,
201
+ widget: widget,
202
+ emitAction: (actionName, payload) => {
203
+ this.widgetAction.emit({ actionName, payload });
204
+ },
205
+ };
206
+ const viewRef = templateRef.createEmbeddedView(context);
207
+ // Attach the view's DOM nodes to the target element
208
+ viewRef.rootNodes.forEach((node) => {
209
+ targetElement.appendChild(node);
210
+ });
211
+ // Store reference for cleanup
212
+ this.dynamicViews.push(viewRef);
213
+ // Trigger change detection
214
+ viewRef.detectChanges();
215
+ }
216
+ /**
217
+ * Cleanup dynamic components and views
218
+ */
219
+ cleanupDynamicWidgets() {
220
+ this.dynamicComponents.forEach((componentRef) => {
221
+ componentRef.destroy();
222
+ });
223
+ this.dynamicComponents = [];
224
+ this.dynamicViews.forEach((viewRef) => {
225
+ viewRef.destroy();
226
+ });
227
+ this.dynamicViews = [];
228
+ }
229
+ setupEventHandlers() {
230
+ if (!this.containerRef?.nativeElement)
231
+ return;
232
+ // Cleanup old resources before setting up new ones
233
+ this.cleanup();
234
+ const container = this.containerRef.nativeElement;
235
+ // Create a custom action handler that emits events
236
+ const actionHandler = {
237
+ handle: async (action, payload) => {
238
+ this.widgetAction.emit({ actionName: action, payload });
239
+ },
240
+ };
241
+ // Attach event handlers using WidgetEventManager
242
+ this.eventManager = new WidgetEventManager(actionHandler);
243
+ this.eventManager.attachHandlers(container);
244
+ }
245
+ handleClick(event) {
246
+ const target = event.target;
247
+ // Only trigger actions on non-form buttons and clickable elements (cards)
248
+ // Don't trigger on input elements or form buttons (let WidgetEventManager handle those)
249
+ const button = target.tagName === 'BUTTON' ? target : target.closest('button');
250
+ if (button && !button.closest('[data-widget-type="form"]')) {
251
+ const actionName = button.getAttribute('data-action');
252
+ if (actionName) {
253
+ try {
254
+ const payloadStr = button.getAttribute('data-payload');
255
+ const payload = payloadStr ? JSON.parse(payloadStr) : {};
256
+ this.widgetAction.emit({ actionName, payload });
257
+ }
258
+ catch (err) {
259
+ console.error('Failed to parse widget action payload:', err);
260
+ }
261
+ }
262
+ }
263
+ }
264
+ /**
265
+ * Cleanup all resources including event listeners.
266
+ */
267
+ cleanup() {
268
+ // Cleanup dynamic widgets first
269
+ this.cleanupDynamicWidgets();
270
+ // Cleanup event manager
271
+ this.eventManager = undefined;
272
+ }
273
+ };
274
+ __decorate([
275
+ Input(),
276
+ __metadata("design:type", Object)
277
+ ], WidgetRendererComponent.prototype, "widgets", void 0);
278
+ __decorate([
279
+ Output(),
280
+ __metadata("design:type", Object)
281
+ ], WidgetRendererComponent.prototype, "widgetAction", void 0);
282
+ __decorate([
283
+ ViewChild('widgetContainer', { static: false }),
284
+ __metadata("design:type", ElementRef)
285
+ ], WidgetRendererComponent.prototype, "containerRef", void 0);
286
+ WidgetRendererComponent = __decorate([
287
+ Component({
288
+ selector: 'bbq-widget-renderer',
289
+ standalone: true,
290
+ imports: [CommonModule],
291
+ template: `
292
+ <div #widgetContainer class="bbq-widgets-container" (click)="handleClick($event)">
293
+ @for (item of widgetItems; track item.index) {
294
+ @if (item.isHtml) {
295
+ <div class="bbq-widget" [innerHTML]="item.html"></div>
296
+ } @else {
297
+ <div class="bbq-widget" #dynamicWidget></div>
298
+ }
299
+ }
300
+ </div>
301
+ `,
302
+ styles: [
303
+ `
304
+ .bbq-widgets-container {
305
+ margin-top: 0.5rem;
306
+ }
307
+
308
+ .bbq-widget {
309
+ margin-bottom: 0.5rem;
310
+ }
311
+ `,
312
+ ],
313
+ })
314
+ ], WidgetRendererComponent);
315
+ export { WidgetRendererComponent };
316
+ //# sourceMappingURL=widget-renderer.component.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"widget-renderer.component.js","sourceRoot":"","sources":["../src/widget-renderer.component.ts"],"names":[],"mappings":"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"}
package/package.json ADDED
@@ -0,0 +1,74 @@
1
+ {
2
+ "name": "@bbq-chat/widgets-angular",
3
+ "version": "1.0.0",
4
+ "description": "Angular components and services for BbQ ChatWidgets",
5
+ "license": "MIT",
6
+ "author": "BbQ ChatWidgets Contributors",
7
+ "homepage": "https://github.com/JeanMarcMbouma/BbQ.ChatWidgets",
8
+ "repository": {
9
+ "type": "git",
10
+ "url": "https://github.com/JeanMarcMbouma/BbQ.ChatWidgets.git",
11
+ "directory": "js-angular"
12
+ },
13
+ "bugs": {
14
+ "url": "https://github.com/JeanMarcMbouma/BbQ.ChatWidgets/issues"
15
+ },
16
+ "keywords": [
17
+ "angular",
18
+ "chat",
19
+ "widgets",
20
+ "ui",
21
+ "components",
22
+ "bbq-chat"
23
+ ],
24
+ "type": "module",
25
+ "main": "./dist/index.js",
26
+ "module": "./dist/index.js",
27
+ "types": "./dist/index.d.ts",
28
+ "exports": {
29
+ ".": {
30
+ "import": "./dist/index.js",
31
+ "require": "./dist/index.cjs",
32
+ "types": "./dist/index.d.ts"
33
+ }
34
+ },
35
+ "files": [
36
+ "dist",
37
+ "src",
38
+ "README.md",
39
+ "LICENSE"
40
+ ],
41
+ "scripts": {
42
+ "build": "tsc && node scripts/build.mjs",
43
+ "build:watch": "tsc --watch",
44
+ "test": "echo \"No tests yet\"",
45
+ "lint": "eslint src --ext .ts",
46
+ "lint:fix": "eslint src --ext .ts --fix",
47
+ "format": "prettier --write \"src/**/*.ts\"",
48
+ "type-check": "tsc --noEmit",
49
+ "prepublishOnly": "npm run build && npm run lint"
50
+ },
51
+ "peerDependencies": {
52
+ "@angular/common": ">=17.0.0",
53
+ "@angular/core": ">=17.0.0",
54
+ "@bbq-chat/widgets": "^1.0.0"
55
+ },
56
+ "devDependencies": {
57
+ "@angular/common": "^17.3.0",
58
+ "@angular/core": "^17.3.0",
59
+ "@bbq-chat/widgets": "file:../js",
60
+ "@types/node": "^20.10.0",
61
+ "@typescript-eslint/eslint-plugin": "^6.13.0",
62
+ "@typescript-eslint/parser": "^6.13.0",
63
+ "esbuild": "^0.27.2",
64
+ "eslint": "^8.55.0",
65
+ "prettier": "^3.1.1",
66
+ "rxjs": "^7.8.0",
67
+ "tslib": "^2.3.0",
68
+ "typescript": "^5.4.2",
69
+ "zone.js": "^0.14.3"
70
+ },
71
+ "engines": {
72
+ "node": ">=14.0.0"
73
+ }
74
+ }
@@ -0,0 +1,120 @@
1
+ import { Type, TemplateRef } from '@angular/core';
2
+ import { ChatWidget } from '@bbq-chat/widgets';
3
+
4
+ /**
5
+ * Context provided to template-based custom widget renderers
6
+ */
7
+ export interface WidgetTemplateContext {
8
+ /**
9
+ * The widget instance being rendered
10
+ */
11
+ $implicit: ChatWidget;
12
+
13
+ /**
14
+ * The widget instance (alternative access)
15
+ */
16
+ widget: ChatWidget;
17
+
18
+ /**
19
+ * Emit a widget action
20
+ */
21
+ emitAction: (actionName: string, payload: unknown) => void;
22
+ }
23
+
24
+ /**
25
+ * Interface for component-based custom widget renderers
26
+ */
27
+ export interface CustomWidgetComponent {
28
+ /**
29
+ * The widget instance to render
30
+ */
31
+ widget: ChatWidget;
32
+
33
+ /**
34
+ * Event emitter for widget actions (optional, will be set by the renderer)
35
+ */
36
+ widgetAction?: (actionName: string, payload: unknown) => void;
37
+ }
38
+
39
+ /**
40
+ * Type for custom widget renderer functions that return HTML strings
41
+ */
42
+ export type CustomWidgetHtmlRenderer = (widget: ChatWidget) => string;
43
+
44
+ /**
45
+ * Type for custom widget renderer configurations
46
+ */
47
+ export type CustomWidgetRenderer =
48
+ | CustomWidgetHtmlRenderer
49
+ | Type<CustomWidgetComponent>
50
+ | TemplateRef<WidgetTemplateContext>;
51
+
52
+ /**
53
+ * Configuration for registering a custom widget renderer
54
+ */
55
+ export interface CustomWidgetRendererConfig {
56
+ /**
57
+ * The widget type identifier
58
+ */
59
+ type: string;
60
+
61
+ /**
62
+ * The renderer: can be a function returning HTML, an Angular Component class, or a TemplateRef
63
+ */
64
+ renderer: CustomWidgetRenderer;
65
+ }
66
+
67
+ /**
68
+ * Type guard to check if a renderer is a TemplateRef
69
+ */
70
+ export function isTemplateRenderer(
71
+ renderer: CustomWidgetRenderer
72
+ ): renderer is TemplateRef<WidgetTemplateContext> {
73
+ return (
74
+ renderer !== null &&
75
+ typeof renderer === 'object' &&
76
+ 'createEmbeddedView' in renderer
77
+ );
78
+ }
79
+
80
+ /**
81
+ * Type guard to check if a renderer is an Angular Component
82
+ *
83
+ * Note: This uses a heuristic check based on the following assumptions:
84
+ * 1. Components are constructor functions
85
+ * 2. Components have a prototype with a constructor property
86
+ * 3. Components typically use dependency injection (no required constructor params)
87
+ *
88
+ * Limitation: This may not detect components with required constructor parameters.
89
+ * For edge cases, explicitly check your component's constructor signature.
90
+ *
91
+ * Alternative: You can always register a wrapper component that has no required params.
92
+ */
93
+ export function isComponentRenderer(
94
+ renderer: CustomWidgetRenderer
95
+ ): renderer is Type<CustomWidgetComponent> {
96
+ // Check if it's a function (constructor) but not a regular function renderer
97
+ if (typeof renderer !== 'function') {
98
+ return false;
99
+ }
100
+
101
+ // Check for Angular component characteristics
102
+ // Components typically have prototype with constructor property
103
+ return (
104
+ renderer.prototype !== undefined &&
105
+ renderer.prototype.constructor === renderer &&
106
+ renderer.length === 0 // Constructor with no required params (Angular DI)
107
+ );
108
+ }
109
+
110
+ /**
111
+ * Type guard to check if a renderer is an HTML function
112
+ *
113
+ * Note: This should be checked AFTER checking for component and template renderers
114
+ * since components are also functions but with additional properties.
115
+ */
116
+ export function isHtmlRenderer(
117
+ renderer: CustomWidgetRenderer
118
+ ): renderer is CustomWidgetHtmlRenderer {
119
+ return typeof renderer === 'function';
120
+ }
package/src/index.ts ADDED
@@ -0,0 +1,65 @@
1
+ /**
2
+ * @bbq-chat/widgets-angular
3
+ *
4
+ * Angular components and services for BbQ ChatWidgets
5
+ *
6
+ * This package provides Angular-native components and services that wrap
7
+ * the core @bbq-chat/widgets library, making it easy to integrate chat
8
+ * widgets into Angular applications.
9
+ *
10
+ * @packageDocumentation
11
+ */
12
+
13
+ // Export components
14
+ export { WidgetRendererComponent } from './widget-renderer.component';
15
+
16
+ // Export services
17
+ export { WidgetRegistryService } from './widget-registry.service';
18
+
19
+ // Export custom widget renderer types
20
+ export type {
21
+ CustomWidgetComponent,
22
+ CustomWidgetRenderer,
23
+ CustomWidgetHtmlRenderer,
24
+ CustomWidgetRendererConfig,
25
+ WidgetTemplateContext,
26
+ } from './custom-widget-renderer.types';
27
+
28
+ export {
29
+ isHtmlRenderer,
30
+ isComponentRenderer,
31
+ isTemplateRenderer,
32
+ } from './custom-widget-renderer.types';
33
+
34
+ // Re-export commonly used types and classes from core package
35
+ export {
36
+ ChatWidget,
37
+ } from '@bbq-chat/widgets';
38
+
39
+ export type {
40
+ ButtonWidget,
41
+ CardWidget,
42
+ FormWidget,
43
+ InputWidget,
44
+ TextAreaWidget,
45
+ DropdownWidget,
46
+ SliderWidget,
47
+ ToggleWidget,
48
+ FileUploadWidget,
49
+ DatePickerWidget,
50
+ MultiSelectWidget,
51
+ ProgressBarWidget,
52
+ ThemeSwitcherWidget,
53
+ ImageWidget,
54
+ ImageCollectionWidget,
55
+ } from '@bbq-chat/widgets';
56
+
57
+ // Re-export utilities
58
+ export {
59
+ SsrWidgetRenderer,
60
+ WidgetEventManager,
61
+ customWidgetRegistry,
62
+ } from '@bbq-chat/widgets';
63
+
64
+ // Version
65
+ export const VERSION = '1.0.0';