@bbq-chat/widgets-angular 1.0.1 → 1.0.3

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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bbq-chat/widgets-angular",
3
- "version": "1.0.1",
3
+ "version": "1.0.3",
4
4
  "description": "Angular components and services for BbQ ChatWidgets",
5
5
  "license": "MIT",
6
6
  "author": "BbQ ChatWidgets Contributors",
@@ -21,56 +21,35 @@
21
21
  "components",
22
22
  "bbq-chat"
23
23
  ],
24
- "main": "./dist/fesm2022/bbq-chat-widgets-angular.mjs",
25
- "module": "./dist/fesm2022/bbq-chat-widgets-angular.mjs",
26
- "esm2022": "./dist/esm2022/bbq-chat-widgets-angular.mjs",
27
- "fesm2022": "./dist/fesm2022/bbq-chat-widgets-angular.mjs",
28
- "typings": "./dist/index.d.ts",
24
+ "sideEffects": false,
25
+ "files": [
26
+ "dist",
27
+ "README.md",
28
+ "LICENSE"
29
+ ],
30
+ "packageManager": "npm@10.9.0",
31
+ "peerDependencies": {
32
+ "@angular/common": "^21.0.0",
33
+ "@angular/compiler": "^21.0.0",
34
+ "@angular/core": "^21.0.0",
35
+ "@angular/forms": "^21.0.0",
36
+ "@angular/platform-browser": "^21.0.0",
37
+ "@angular/router": "^21.0.0",
38
+ "rxjs": "~7.8.0",
39
+ "@bbq-chat/widgets": "^1.0.0"
40
+ },
41
+ "module": "fesm2022/index.mjs",
42
+ "typings": "types/index.d.ts",
29
43
  "exports": {
30
44
  "./package.json": {
31
45
  "default": "./package.json"
32
46
  },
33
47
  ".": {
34
- "types": "./dist/index.d.ts",
35
- "esm2022": "./dist/esm2022/bbq-chat-widgets-angular.mjs",
36
- "esm": "./dist/esm2022/bbq-chat-widgets-angular.mjs",
37
- "default": "./dist/fesm2022/bbq-chat-widgets-angular.mjs"
48
+ "types": "./types/index.d.ts",
49
+ "default": "./fesm2022/index.mjs"
38
50
  }
39
51
  },
40
- "sideEffects": false,
41
- "scripts": {
42
- "build": "ng-packagr -p ng-package.json",
43
- "build:watch": "ng-packagr -p ng-package.json --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": ">=19.2.17",
53
- "@angular/core": ">=19.2.17",
54
- "@bbq-chat/widgets": "^1.0.0"
55
- },
56
- "devDependencies": {
57
- "@angular/common": "^19.2.17",
58
- "@angular/compiler": "^19.2.17",
59
- "@angular/compiler-cli": "^19.2.17",
60
- "@angular/core": "^19.2.17",
61
- "@bbq-chat/widgets": "file:../js",
62
- "@types/node": "^20.10.0",
63
- "@typescript-eslint/eslint-plugin": "^6.13.0",
64
- "@typescript-eslint/parser": "^6.13.0",
65
- "eslint": "^8.55.0",
66
- "ng-packagr": "^19.0.0",
67
- "prettier": "^3.1.1",
68
- "rxjs": "^7.8.0",
69
- "tslib": "^2.3.0",
70
- "typescript": "~5.7.0",
71
- "zone.js": "^0.15.0"
72
- },
73
- "engines": {
74
- "node": ">=14.0.0"
52
+ "dependencies": {
53
+ "tslib": "^2.3.0"
75
54
  }
76
- }
55
+ }
package/.eslintrc.json DELETED
@@ -1,23 +0,0 @@
1
- {
2
- "root": true,
3
- "parser": "@typescript-eslint/parser",
4
- "parserOptions": {
5
- "ecmaVersion": 2020,
6
- "sourceType": "module",
7
- "project": "./tsconfig.json"
8
- },
9
- "plugins": ["@typescript-eslint"],
10
- "extends": [
11
- "eslint:recommended",
12
- "plugin:@typescript-eslint/recommended"
13
- ],
14
- "rules": {
15
- "@typescript-eslint/no-explicit-any": "off",
16
- "@typescript-eslint/explicit-module-boundary-types": "off"
17
- },
18
- "env": {
19
- "browser": true,
20
- "es2020": true,
21
- "node": true
22
- }
23
- }
package/.prettierrc.json DELETED
@@ -1,8 +0,0 @@
1
- {
2
- "semi": true,
3
- "trailingComma": "es5",
4
- "singleQuote": true,
5
- "printWidth": 100,
6
- "tabWidth": 2,
7
- "useTabs": false
8
- }
package/EXAMPLES.md DELETED
@@ -1,468 +0,0 @@
1
- # Usage Examples
2
-
3
- ## Basic Usage
4
-
5
- ### 1. Install the package
6
-
7
- ```bash
8
- npm install @bbq-chat/widgets-angular @bbq-chat/widgets
9
- ```
10
-
11
- ### 2. Import styles in your global styles file
12
-
13
- In `styles.css` or `styles.scss`:
14
-
15
- ```css
16
- @import '@bbq-chat/widgets/styles';
17
- ```
18
-
19
- ### 3. Create a chat component
20
-
21
- ```typescript
22
- import { Component } from '@angular/core';
23
- import { CommonModule } from '@angular/common';
24
- import { WidgetRendererComponent } from '@bbq-chat/widgets-angular';
25
- import type { ChatWidget } from '@bbq-chat/widgets-angular';
26
-
27
- @Component({
28
- selector: 'app-chat',
29
- standalone: true,
30
- imports: [CommonModule, WidgetRendererComponent],
31
- template: `
32
- <div class="chat-container">
33
- <div class="messages">
34
- @for (message of messages; track message.id) {
35
- <div class="message">
36
- <div class="message-content">{{ message.content }}</div>
37
- <bbq-widget-renderer
38
- [widgets]="message.widgets"
39
- (widgetAction)="handleWidgetAction($event)">
40
- </bbq-widget-renderer>
41
- </div>
42
- }
43
- </div>
44
-
45
- <div class="input-area">
46
- <input
47
- type="text"
48
- [(ngModel)]="userInput"
49
- (keyup.enter)="sendMessage()"
50
- placeholder="Type a message..."
51
- />
52
- <button (click)="sendMessage()">Send</button>
53
- </div>
54
- </div>
55
- `,
56
- styles: [`
57
- .chat-container {
58
- display: flex;
59
- flex-direction: column;
60
- height: 100vh;
61
- max-width: 800px;
62
- margin: 0 auto;
63
- }
64
-
65
- .messages {
66
- flex: 1;
67
- overflow-y: auto;
68
- padding: 1rem;
69
- }
70
-
71
- .message {
72
- margin-bottom: 1rem;
73
- }
74
-
75
- .message-content {
76
- margin-bottom: 0.5rem;
77
- }
78
-
79
- .input-area {
80
- display: flex;
81
- gap: 0.5rem;
82
- padding: 1rem;
83
- border-top: 1px solid #ccc;
84
- }
85
-
86
- input {
87
- flex: 1;
88
- padding: 0.5rem;
89
- }
90
- `]
91
- })
92
- export class ChatComponent {
93
- messages: Array<{ id: number; content: string; widgets?: ChatWidget[] }> = [];
94
- userInput = '';
95
- private messageId = 0;
96
-
97
- async sendMessage() {
98
- if (!this.userInput.trim()) return;
99
-
100
- // Add user message
101
- this.messages.push({
102
- id: this.messageId++,
103
- content: this.userInput,
104
- });
105
-
106
- const userMessage = this.userInput;
107
- this.userInput = '';
108
-
109
- // Send to backend
110
- try {
111
- const response = await fetch('/api/chat/message', {
112
- method: 'POST',
113
- headers: { 'Content-Type': 'application/json' },
114
- body: JSON.stringify({ message: userMessage }),
115
- });
116
-
117
- const data = await response.json();
118
-
119
- // Add assistant response with widgets
120
- this.messages.push({
121
- id: this.messageId++,
122
- content: data.content || '',
123
- widgets: data.widgets || [],
124
- });
125
- } catch (error) {
126
- console.error('Failed to send message:', error);
127
- }
128
- }
129
-
130
- async handleWidgetAction(event: { actionName: string; payload: any }) {
131
- console.log('Widget action:', event);
132
-
133
- try {
134
- const response = await fetch('/api/chat/action', {
135
- method: 'POST',
136
- headers: { 'Content-Type': 'application/json' },
137
- body: JSON.stringify({
138
- action: event.actionName,
139
- payload: event.payload,
140
- }),
141
- });
142
-
143
- const data = await response.json();
144
-
145
- // Add response with new widgets
146
- this.messages.push({
147
- id: this.messageId++,
148
- content: data.content || '',
149
- widgets: data.widgets || [],
150
- });
151
- } catch (error) {
152
- console.error('Failed to handle widget action:', error);
153
- }
154
- }
155
- }
156
- ```
157
-
158
- ## Using Custom Widgets
159
-
160
- ### 1. Define your custom widget
161
-
162
- ```typescript
163
- // custom-widgets/weather-widget.ts
164
- import { ChatWidget } from '@bbq-chat/widgets-angular';
165
-
166
- export class WeatherWidget extends ChatWidget {
167
- override type = 'weather';
168
-
169
- constructor(
170
- public label: string,
171
- public action: string,
172
- public city: string,
173
- public temperature: number,
174
- public condition: string
175
- ) {
176
- super(label, action);
177
- }
178
- }
179
- ```
180
-
181
- ### 2. Register the custom widget
182
-
183
- ```typescript
184
- // app.config.ts or component
185
- import { Component, OnInit } from '@angular/core';
186
- import { WidgetRegistryService } from '@bbq-chat/widgets-angular';
187
- import { WeatherWidget } from './custom-widgets/weather-widget';
188
-
189
- @Component({
190
- selector: 'app-root',
191
- template: '...'
192
- })
193
- export class AppComponent implements OnInit {
194
- constructor(private widgetRegistry: WidgetRegistryService) {}
195
-
196
- ngOnInit() {
197
- // Register custom widget factory
198
- this.widgetRegistry.registerFactory('weather', (obj: any) => {
199
- if (obj.type === 'weather') {
200
- return new WeatherWidget(
201
- obj.label,
202
- obj.action,
203
- obj.city,
204
- obj.temperature,
205
- obj.condition
206
- );
207
- }
208
- return null;
209
- });
210
- }
211
- }
212
- ```
213
-
214
- ### 3. Create custom renderers
215
-
216
- The library now supports three types of custom renderers:
217
-
218
- #### Option A: HTML Function Renderer
219
-
220
- Use a function that returns HTML strings:
221
-
222
- ```typescript
223
- import { Component, OnInit } from '@angular/core';
224
- import { WidgetRegistryService } from '@bbq-chat/widgets-angular';
225
- import { WeatherWidget } from './custom-widgets/weather-widget';
226
-
227
- @Component({
228
- selector: 'app-root',
229
- template: '...'
230
- })
231
- export class AppComponent implements OnInit {
232
- constructor(private widgetRegistry: WidgetRegistryService) {}
233
-
234
- ngOnInit() {
235
- // Register custom widget factory
236
- this.widgetRegistry.registerFactory('weather', (obj: any) => {
237
- if (obj.type === 'weather') {
238
- return new WeatherWidget(
239
- obj.label,
240
- obj.action,
241
- obj.city,
242
- obj.temperature,
243
- obj.condition
244
- );
245
- }
246
- return null;
247
- });
248
-
249
- // Register HTML renderer
250
- this.widgetRegistry.registerRenderer('weather', (widget) => {
251
- const w = widget as WeatherWidget;
252
- return `
253
- <div class="weather-widget">
254
- <h3>${w.city}</h3>
255
- <p>${w.temperature}°C - ${w.condition}</p>
256
- </div>
257
- `;
258
- });
259
- }
260
- }
261
- ```
262
-
263
- #### Option B: Angular Component Renderer (Recommended)
264
-
265
- Create an Angular component for better type safety and data binding:
266
-
267
- ```typescript
268
- // weather-widget.component.ts
269
- import { Component, Input } from '@angular/core';
270
- import { CommonModule } from '@angular/common';
271
- import { CustomWidgetComponent } from '@bbq-chat/widgets-angular';
272
- import { WeatherWidget } from './custom-widgets/weather-widget';
273
-
274
- @Component({
275
- selector: 'app-weather-widget',
276
- standalone: true,
277
- imports: [CommonModule],
278
- template: `
279
- <div class="weather-widget">
280
- <h3>{{ weatherWidget.city }}</h3>
281
- <div class="weather-info">
282
- <p class="temperature">{{ weatherWidget.temperature }}°C</p>
283
- <p class="condition">{{ weatherWidget.condition }}</p>
284
- </div>
285
- <button (click)="onRefresh()">Refresh</button>
286
- </div>
287
- `,
288
- styles: [`
289
- .weather-widget {
290
- padding: 1rem;
291
- border: 1px solid #ccc;
292
- border-radius: 8px;
293
- background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
294
- color: white;
295
- }
296
- .temperature {
297
- font-size: 2rem;
298
- font-weight: bold;
299
- }
300
- `]
301
- })
302
- export class WeatherWidgetComponent implements CustomWidgetComponent {
303
- @Input() widget!: ChatWidget;
304
- widgetAction?: (actionName: string, payload: unknown) => void;
305
-
306
- get weatherWidget(): WeatherWidget {
307
- return this.widget as WeatherWidget;
308
- }
309
-
310
- onRefresh() {
311
- if (this.widgetAction) {
312
- this.widgetAction('refresh_weather', {
313
- city: this.weatherWidget.city
314
- });
315
- }
316
- }
317
- }
318
- ```
319
-
320
- Then register the component:
321
-
322
- ```typescript
323
- import { Component, OnInit } from '@angular/core';
324
- import { WidgetRegistryService } from '@bbq-chat/widgets-angular';
325
- import { WeatherWidget } from './custom-widgets/weather-widget';
326
- import { WeatherWidgetComponent } from './weather-widget.component';
327
-
328
- @Component({
329
- selector: 'app-root',
330
- template: '...'
331
- })
332
- export class AppComponent implements OnInit {
333
- constructor(private widgetRegistry: WidgetRegistryService) {}
334
-
335
- ngOnInit() {
336
- // Register custom widget factory
337
- this.widgetRegistry.registerFactory('weather', (obj: any) => {
338
- if (obj.type === 'weather') {
339
- return new WeatherWidget(
340
- obj.label,
341
- obj.action,
342
- obj.city,
343
- obj.temperature,
344
- obj.condition
345
- );
346
- }
347
- return null;
348
- });
349
-
350
- // Register component renderer - enables full Angular features
351
- this.widgetRegistry.registerRenderer('weather', WeatherWidgetComponent);
352
- }
353
- }
354
- ```
355
-
356
- #### Option C: Angular Template Renderer
357
-
358
- Use Angular templates for inline rendering with data binding:
359
-
360
- ```typescript
361
- import { Component, OnInit, ViewChild, TemplateRef } from '@angular/core';
362
- import { WidgetRegistryService, WidgetTemplateContext } from '@bbq-chat/widgets-angular';
363
- import { WeatherWidget } from './custom-widgets/weather-widget';
364
-
365
- @Component({
366
- selector: 'app-root',
367
- template: `
368
- <ng-template #weatherTemplate let-widget let-emitAction="emitAction">
369
- <div class="weather-widget">
370
- <h3>{{ widget.city }}</h3>
371
- <p>{{ widget.temperature }}°C - {{ widget.condition }}</p>
372
- <button (click)="emitAction('refresh_weather', { city: widget.city })">
373
- Refresh
374
- </button>
375
- </div>
376
- </ng-template>
377
-
378
- <bbq-widget-renderer
379
- [widgets]="widgets"
380
- (widgetAction)="handleWidgetAction($event)">
381
- </bbq-widget-renderer>
382
- `
383
- })
384
- export class AppComponent implements OnInit {
385
- @ViewChild('weatherTemplate', { static: true })
386
- weatherTemplate!: TemplateRef<WidgetTemplateContext>;
387
-
388
- widgets: ChatWidget[] = [];
389
-
390
- constructor(private widgetRegistry: WidgetRegistryService) {}
391
-
392
- ngOnInit() {
393
- // Register custom widget factory
394
- this.widgetRegistry.registerFactory('weather', (obj: any) => {
395
- if (obj.type === 'weather') {
396
- return new WeatherWidget(
397
- obj.label,
398
- obj.action,
399
- obj.city,
400
- obj.temperature,
401
- obj.condition
402
- );
403
- }
404
- return null;
405
- });
406
-
407
- // Register template renderer - enables inline Angular templates
408
- this.widgetRegistry.registerRenderer('weather', this.weatherTemplate);
409
- }
410
-
411
- handleWidgetAction(event: { actionName: string; payload: any }) {
412
- console.log('Widget action:', event);
413
- }
414
- }
415
- ```
416
-
417
- ### Benefits of Component-Based Renderers
418
-
419
- Using Angular components for custom widgets provides several advantages:
420
-
421
- 1. **Type Safety**: Full TypeScript support with interfaces and types
422
- 2. **Data Binding**: Use Angular's powerful two-way data binding
423
- 3. **Lifecycle Hooks**: Access to Angular lifecycle hooks (ngOnInit, ngOnDestroy, etc.)
424
- 4. **Change Detection**: Automatic UI updates when data changes
425
- 5. **Dependency Injection**: Inject services directly into widget components
426
- 6. **Animations**: Use Angular animations for smooth transitions
427
- 7. **Reactive Forms**: Integrate with Angular reactive forms
428
- 8. **Testing**: Easier unit testing with Angular TestBed
429
-
430
- ## Integration with Forms
431
-
432
- The package automatically handles form submissions. Example:
433
-
434
- ```typescript
435
- handleWidgetAction(event: { actionName: string; payload: any }) {
436
- if (event.actionName === 'submit_form') {
437
- console.log('Form data:', event.payload);
438
- // payload contains all form field values
439
- // { name: 'John', email: 'john@example.com', ... }
440
- }
441
- }
442
- ```
443
-
444
- ## Theming
445
-
446
- Import different themes:
447
-
448
- ```css
449
- /* Light theme (default) */
450
- @import '@bbq-chat/widgets/styles/light';
451
-
452
- /* Dark theme */
453
- @import '@bbq-chat/widgets/styles/dark';
454
-
455
- /* Corporate theme */
456
- @import '@bbq-chat/widgets/styles/corporate';
457
- ```
458
-
459
- Or customize with CSS variables:
460
-
461
- ```css
462
- :root {
463
- --bbq-primary-color: #007bff;
464
- --bbq-secondary-color: #6c757d;
465
- --bbq-border-radius: 8px;
466
- /* ... other variables */
467
- }
468
- ```
package/ng-package.json DELETED
@@ -1,10 +0,0 @@
1
- {
2
- "$schema": "node_modules/ng-packagr/ng-package.schema.json",
3
- "dest": "dist",
4
- "lib": {
5
- "entryFile": "src/public_api.ts"
6
- },
7
- "allowedNonPeerDependencies": [
8
- "."
9
- ]
10
- }
package/scripts/build.mjs DELETED
@@ -1,63 +0,0 @@
1
- import esbuild from 'esbuild';
2
- import fs from 'fs';
3
- import path from 'path';
4
-
5
- const buildOptions = {
6
- entryPoints: ['dist/index.js'],
7
- outdir: 'dist',
8
- outExtension: { '.js': '.cjs' },
9
- bundle: false,
10
- format: 'cjs',
11
- platform: 'browser',
12
- target: 'es2020',
13
- };
14
-
15
- async function build() {
16
- try {
17
- // Build CJS versions
18
- await esbuild.build(buildOptions);
19
- console.log('✓ Built CommonJS versions');
20
-
21
- // Ensure package.json is in dist
22
- const packageJson = JSON.parse(fs.readFileSync('package.json', 'utf-8'));
23
- const distPackageJson = {
24
- name: packageJson.name,
25
- version: packageJson.version,
26
- description: packageJson.description,
27
- license: packageJson.license,
28
- main: './index.js',
29
- module: './index.js',
30
- types: './index.d.ts',
31
- exports: {
32
- '.': {
33
- import: './index.js',
34
- require: './index.cjs',
35
- types: './index.d.ts',
36
- },
37
- },
38
- };
39
-
40
- fs.writeFileSync('dist/package.json', JSON.stringify(distPackageJson, null, 2));
41
- console.log('✓ Created dist/package.json');
42
-
43
- // Copy README
44
- if (fs.existsSync('README.md')) {
45
- fs.copyFileSync('README.md', 'dist/README.md');
46
- console.log('✓ Copied README.md');
47
- }
48
-
49
- // Copy LICENSE from root
50
- const rootLicense = path.join('..', 'LICENSE');
51
- if (fs.existsSync(rootLicense)) {
52
- fs.copyFileSync(rootLicense, 'dist/LICENSE');
53
- console.log('✓ Copied LICENSE');
54
- }
55
-
56
- console.log('✓ Build complete!');
57
- } catch (error) {
58
- console.error('Build failed:', error);
59
- process.exit(1);
60
- }
61
- }
62
-
63
- build();