@bbq-chat/widgets-angular 1.0.9 → 1.0.10

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.
Files changed (42) hide show
  1. package/fesm2022/index.mjs +2132 -0
  2. package/fesm2022/index.mjs.map +1 -0
  3. package/package.json +15 -20
  4. package/types/index.d.ts +659 -0
  5. package/.angular/cache/21.0.5/ng-packagr/97cbacd0e5e4cb18d1fead4d7f3aee1c3863ba3ffbe7cb7dd7780f237a848a5c +0 -1
  6. package/.angular/cache/21.0.5/ng-packagr/tsbuildinfo/index.tsbuildinfo +0 -1
  7. package/.eslintrc.json +0 -23
  8. package/.prettierrc.json +0 -8
  9. package/EXAMPLES.md +0 -484
  10. package/angular.json +0 -36
  11. package/ng-package.json +0 -9
  12. package/src/angular-widget-renderer.spec.ts +0 -157
  13. package/src/components/button.component.ts +0 -35
  14. package/src/components/card.component.ts +0 -52
  15. package/src/components/datepicker.component.ts +0 -63
  16. package/src/components/dropdown.component.ts +0 -65
  17. package/src/components/fileupload.component.ts +0 -71
  18. package/src/components/form.component.ts +0 -433
  19. package/src/components/image.component.ts +0 -33
  20. package/src/components/imagecollection.component.ts +0 -39
  21. package/src/components/index.ts +0 -20
  22. package/src/components/input.component.ts +0 -63
  23. package/src/components/multiselect.component.ts +0 -67
  24. package/src/components/progressbar.component.ts +0 -50
  25. package/src/components/slider.component.ts +0 -67
  26. package/src/components/textarea.component.ts +0 -63
  27. package/src/components/themeswitcher.component.ts +0 -46
  28. package/src/components/toggle.component.ts +0 -63
  29. package/src/custom-widget-renderer.types.ts +0 -120
  30. package/src/examples/form-validation-listener.component.ts +0 -41
  31. package/src/public_api.ts +0 -107
  32. package/src/renderers/AngularWidgetRenderer.ts +0 -100
  33. package/src/renderers/built-in-components.ts +0 -41
  34. package/src/renderers/index.ts +0 -7
  35. package/src/services/form-validation.service.ts +0 -21
  36. package/src/widget-di.tokens.ts +0 -95
  37. package/src/widget-registry.service.ts +0 -128
  38. package/src/widget-renderer.component.ts +0 -421
  39. package/tsconfig.json +0 -37
  40. package/tsconfig.lib.json +0 -18
  41. package/tsconfig.lib.prod.json +0 -11
  42. package/tsconfig.spec.json +0 -13
@@ -1,433 +0,0 @@
1
- import { Component, Input, Output, EventEmitter, OnInit, AfterViewInit, ViewChildren, QueryList, ViewContainerRef, ComponentRef, Injector, EnvironmentInjector, createComponent, Type, OnDestroy, ElementRef } from '@angular/core';
2
- import { FormValidationService } from '../services/form-validation.service';
3
- import { CommonModule } from '@angular/common';
4
- import { FormsModule } from '@angular/forms';
5
- import type { FormWidget, ChatWidget } from '@bbq-chat/widgets';
6
- import { CustomWidgetComponent } from '../custom-widget-renderer.types';
7
- import { InputWidgetComponent } from './input.component';
8
- import { TextAreaWidgetComponent } from './textarea.component';
9
- import { DropdownWidgetComponent } from './dropdown.component';
10
- import { SliderWidgetComponent } from './slider.component';
11
- import { ToggleWidgetComponent } from './toggle.component';
12
- import { DatePickerWidgetComponent } from './datepicker.component';
13
- import { MultiSelectWidgetComponent } from './multiselect.component';
14
- import { FileUploadWidgetComponent } from './fileupload.component';
15
-
16
- /**
17
- * Helper class to wrap form fields as widgets for dynamic rendering
18
- */
19
- class FormFieldWidget implements ChatWidget {
20
- readonly type: string;
21
- readonly label: string;
22
- readonly action: string;
23
- readonly appearance = 'form';
24
- readonly hideLabel = true;
25
-
26
- constructor(
27
- public field: any,
28
- public formId: string
29
- ) {
30
- this.type = this.mapFieldTypeToWidgetType(field.type);
31
- this.label = field.label;
32
- this.action = `${formId}_${field.name}`;
33
- }
34
-
35
- private mapFieldTypeToWidgetType(fieldType: string): string {
36
- const typeMap: Record<string, string> = {
37
- 'input': 'input',
38
- 'text': 'input',
39
- 'email': 'input',
40
- 'number': 'input',
41
- 'password': 'input',
42
- 'textarea': 'textarea',
43
- 'dropdown': 'dropdown',
44
- 'select': 'dropdown',
45
- 'slider': 'slider',
46
- 'toggle': 'toggle',
47
- 'datepicker': 'datepicker',
48
- 'multiselect': 'multiselect',
49
- 'fileupload': 'fileupload'
50
- };
51
- return typeMap[fieldType] || 'input';
52
- }
53
-
54
- // Map field properties to widget properties
55
- get placeholder(): string | undefined {
56
- return this.field.placeholder ?? undefined;
57
- }
58
-
59
- get maxLength(): number | undefined {
60
- return this.field['maxLength'];
61
- }
62
-
63
- get rows(): number | undefined {
64
- return this.field['rows'];
65
- }
66
-
67
- get options(): string[] {
68
- return this.field['options'] || [];
69
- }
70
-
71
- get min(): number {
72
- return this.field['min'] ?? 0;
73
- }
74
-
75
- get max(): number {
76
- return this.field['max'] ?? 100;
77
- }
78
-
79
- get step(): number {
80
- return this.field['step'] ?? 1;
81
- }
82
-
83
- get defaultValue(): any {
84
- return this.field['defaultValue'] ?? (this.type === 'slider' ? this.min : undefined);
85
- }
86
-
87
- get minDate(): string | undefined {
88
- return this.field['minDate'];
89
- }
90
-
91
- get maxDate(): string | undefined {
92
- return this.field['maxDate'];
93
- }
94
-
95
- get accept(): string | undefined {
96
- return this.field['accept'];
97
- }
98
-
99
- get maxBytes(): number | undefined {
100
- return this.field['maxBytes'];
101
- }
102
-
103
- // ChatWidget interface methods
104
- toJson(): string {
105
- return JSON.stringify(this.toObject());
106
- }
107
-
108
- toObject(): any {
109
- return {
110
- type: this.type,
111
- label: this.label,
112
- action: this.action,
113
- ...this.field
114
- };
115
- }
116
- }
117
-
118
- @Component({
119
- selector: 'bbq-form-widget',
120
- standalone: true,
121
- imports: [CommonModule, FormsModule],
122
- template: `
123
- <div
124
- class="bbq-widget bbq-form"
125
- [class.bbq-form-submitted]="isSubmitted"
126
- [attr.aria-disabled]="isSubmitted ? 'true' : null"
127
- [attr.data-widget-id]="formId"
128
- [attr.data-widget-type]="'form'"
129
- [attr.data-action]="formWidget.action">
130
- <fieldset class="bbq-form-fieldset">
131
- <legend class="bbq-form-title">{{ formWidget.title }}</legend>
132
-
133
- @for (field of formWidget.fields; track field.name) {
134
- <div
135
- class="bbq-form-field"
136
- [class.bbq-form-field-required]="field.required"
137
- [attr.data-required]="field.required ? 'true' : null">
138
- <label class="bbq-form-field-label" [attr.for]="getFieldId(field.name)">
139
- {{ field.label }}
140
- @if (field.required) {
141
- <span class="bbq-form-required">*</span>
142
- }
143
- </label>
144
-
145
- <div #fieldContainer class="bbq-form-field-widget"></div>
146
-
147
- @if (getFieldProp(field, 'validationHint')) {
148
- <span class="bbq-form-field-hint">{{ getFieldProp(field, 'validationHint') }}</span>
149
- }
150
- </div>
151
- }
152
-
153
- <div class="bbq-form-validation-message" [style.display]="showValidationMessage ? 'block' : 'none'">
154
- Please fill in all required fields before submitting.
155
- </div>
156
-
157
- @if (formWidget.actions && formWidget.actions.length > 0) {
158
- <div class="bbq-form-actions">
159
- @for (action of formWidget.actions; track action.label) {
160
- <button
161
- type="button"
162
- class="bbq-form-button"
163
- [class.bbq-form-submit]="action.type === 'submit'"
164
- [class.bbq-form-cancel]="action.type !== 'submit'"
165
- [attr.data-action]="formWidget.action"
166
- [attr.data-action-type]="action.type"
167
- [disabled]="isSubmitted"
168
- (click)="onActionClick(action.type)">
169
- {{ action.label }}
170
- </button>
171
- }
172
- </div>
173
- }
174
- </fieldset>
175
- </div>
176
- `,
177
- styles: [`
178
- .bbq-form-field-widget {
179
- display: contents;
180
- }
181
- .bbq-form-submitted {
182
- opacity: 0.7;
183
- }
184
- `]
185
- })
186
- export class FormWidgetComponent implements CustomWidgetComponent, OnInit, AfterViewInit, OnDestroy {
187
- @Input() widget!: any;
188
- widgetAction?: (actionName: string, payload: unknown) => void;
189
- @Input() fieldComponentRegistryOverride?: Record<string, Type<CustomWidgetComponent>>;
190
- @Output() validationState = new EventEmitter<{ valid: boolean; errors: Array<{ field: string; reason?: string }> }>();
191
-
192
- @ViewChildren('fieldContainer', { read: ViewContainerRef })
193
- fieldContainers!: QueryList<ViewContainerRef>;
194
-
195
- formId = '';
196
- formData: Record<string, any> = {};
197
- showValidationMessage = false;
198
- private componentRefs: ComponentRef<any>[] = [];
199
-
200
- // Component registry for field types (can be extended via `fieldComponentRegistryOverride`)
201
- private fieldComponentRegistry: Record<string, Type<CustomWidgetComponent>> = {
202
- 'input': InputWidgetComponent,
203
- 'text': InputWidgetComponent,
204
- 'email': InputWidgetComponent,
205
- 'number': InputWidgetComponent,
206
- 'password': InputWidgetComponent,
207
- 'textarea': TextAreaWidgetComponent,
208
- 'dropdown': DropdownWidgetComponent,
209
- 'select': DropdownWidgetComponent,
210
- 'slider': SliderWidgetComponent,
211
- 'toggle': ToggleWidgetComponent,
212
- 'datepicker': DatePickerWidgetComponent,
213
- 'multiselect': MultiSelectWidgetComponent,
214
- 'fileupload': FileUploadWidgetComponent,
215
- 'checkbox': ToggleWidgetComponent,
216
- 'radio': ToggleWidgetComponent,
217
- };
218
-
219
- // Whether the form has been submitted; when true, user interaction is disabled
220
- isSubmitted = false;
221
-
222
- get formWidget(): FormWidget {
223
- return this.widget as FormWidget;
224
- }
225
-
226
- constructor(
227
- private injector: Injector,
228
- private environmentInjector: EnvironmentInjector
229
- , private hostRef: ElementRef
230
- , private formValidationService: FormValidationService
231
- ) {}
232
-
233
- ngOnInit() {
234
- this.formId = `bbq-${this.formWidget.action.replace(/\s+/g, '-').toLowerCase()}`;
235
-
236
- // Initialize form data with default values
237
- for (const field of this.formWidget.fields || []) {
238
- if (field.type === 'slider') {
239
- this.formData[field.name] = field['default'] ?? field['defaultValue'] ?? field['min'] ?? 0;
240
- } else if (field.type === 'toggle' || field.type === 'checkbox') {
241
- this.formData[field.name] = field['defaultValue'] ?? false;
242
- } else if (field.type === 'multiselect') {
243
- this.formData[field.name] = [];
244
- } else {
245
- this.formData[field.name] = '';
246
- }
247
- }
248
-
249
- // Merge any overrides provided by the consumer
250
- if (this.fieldComponentRegistryOverride) {
251
- this.fieldComponentRegistry = { ...this.fieldComponentRegistry, ...this.fieldComponentRegistryOverride };
252
- }
253
- }
254
-
255
- ngAfterViewInit() {
256
- // Render field widgets dynamically
257
- setTimeout(() => this.renderFieldWidgets(), 0);
258
- }
259
-
260
- ngOnDestroy() {
261
- // Clean up component refs
262
- this.componentRefs.forEach(ref => ref.destroy());
263
- this.componentRefs = [];
264
- }
265
-
266
- private renderFieldWidgets() {
267
- const containers = this.fieldContainers.toArray();
268
- const fields = this.formWidget.fields || [];
269
-
270
- fields.forEach((field: any, index: number) => {
271
- const container = containers[index];
272
- if (!container) return;
273
-
274
- const componentType = this.fieldComponentRegistry[field.type];
275
- if (!componentType) {
276
- // Fallback to input for unknown types
277
- this.renderInputFallback(container, field);
278
- return;
279
- }
280
-
281
- // Create the field widget
282
- const fieldWidget = new FormFieldWidget(field, this.formId);
283
-
284
- // Create the component
285
- const componentRef = createComponent(componentType, {
286
- environmentInjector: this.environmentInjector,
287
- elementInjector: this.injector,
288
- });
289
-
290
- // Set component inputs
291
- const instance = componentRef.instance as any;
292
- instance['widget'] = fieldWidget;
293
- // Pass current disabled state so custom components can opt-in to being readonly
294
- instance['disabled'] = this.isSubmitted;
295
-
296
- // Connect to form data via widgetAction
297
- instance['widgetAction'] = (actionName: string, payload: unknown) => {
298
- // Handle field value changes - for now, we'll sync via the rendered widget's internal state
299
- // The actual form submission will gather values from the DOM
300
- };
301
-
302
- // Attach to container
303
- container.insert(componentRef.hostView);
304
- this.componentRefs.push(componentRef);
305
-
306
- // Trigger change detection
307
- componentRef.changeDetectorRef.detectChanges();
308
- });
309
- }
310
-
311
- private renderInputFallback(container: ViewContainerRef, field: any) {
312
- // For unsupported field types, render a basic input
313
- const fieldWidget = new FormFieldWidget(field, this.formId);
314
- const componentRef = createComponent(InputWidgetComponent, {
315
- environmentInjector: this.environmentInjector,
316
- elementInjector: this.injector,
317
- });
318
-
319
- const instance = componentRef.instance as any;
320
- instance['widget'] = fieldWidget;
321
- instance['disabled'] = this.isSubmitted;
322
-
323
- container.insert(componentRef.hostView);
324
- this.componentRefs.push(componentRef);
325
- componentRef.changeDetectorRef.detectChanges();
326
- }
327
-
328
- getFieldId(fieldName: string): string {
329
- // Match the ID format used by dynamically rendered input widgets
330
- return `bbq-${this.formId}_${fieldName}-input`;
331
- }
332
-
333
- getFieldProp(field: any, prop: string): any {
334
- return field[prop];
335
- }
336
-
337
- onActionClick(actionType: string) {
338
- if (this.isSubmitted) return;
339
- if (actionType === 'submit') {
340
- // Validate required fields
341
- const hasErrors = this.validateForm();
342
-
343
- if (hasErrors) {
344
- this.showValidationMessage = true;
345
- return;
346
- }
347
-
348
- this.showValidationMessage = false;
349
-
350
- // Mark submitted to prevent further interaction
351
- this.isSubmitted = true;
352
- // Inform child components and disable DOM controls
353
- this.componentRefs.forEach(ref => {
354
- try {
355
- (ref.instance as any)['disabled'] = true;
356
- ref.changeDetectorRef.detectChanges();
357
- } catch { }
358
- });
359
- this.disableFormInteraction();
360
-
361
- // Gather form data from the DOM (since widgets manage their own state)
362
- this.gatherFormData();
363
-
364
- if (this.widgetAction) {
365
- this.widgetAction(this.formWidget.action, this.formData);
366
- }
367
- } else {
368
- // Cancel or other actions
369
- if (this.widgetAction) {
370
- this.widgetAction(this.formWidget.action, { actionType });
371
- }
372
- }
373
- }
374
-
375
- private validateForm(): boolean {
376
- const errors: Array<{ field: string; reason?: string }> = [];
377
- for (const field of this.formWidget.fields || []) {
378
- if (field.required) {
379
- const value = this.formData[field.name];
380
- if (value === undefined || value === null || value === '' ||
381
- (Array.isArray(value) && value.length === 0)) {
382
- errors.push({ field: field.name, reason: 'required' });
383
- }
384
- }
385
- }
386
-
387
- const hasErrors = errors.length > 0;
388
- const payload = { formId: this.formId, valid: !hasErrors, errors };
389
- // Emit to the local Output for direct consumers
390
- this.validationState.emit({ valid: !hasErrors, errors });
391
- // Also publish via the shared service so consumers that don't have direct access
392
- // to the component instance can subscribe app-wide.
393
- try { this.formValidationService.emit(payload); } catch { }
394
- return hasErrors; // true when there are errors
395
- }
396
-
397
- private disableFormInteraction() {
398
- try {
399
- const root: HTMLElement = this.hostRef?.nativeElement;
400
- if (!root) return;
401
- const controls = root.querySelectorAll('input,select,textarea,button');
402
- controls.forEach((el: Element) => {
403
- try { (el as HTMLInputElement | HTMLSelectElement | HTMLTextAreaElement | HTMLButtonElement).disabled = true; } catch {}
404
- });
405
- root.setAttribute('aria-disabled', 'true');
406
- } catch { }
407
- }
408
-
409
- private gatherFormData() {
410
- // Gather data from the rendered field widgets
411
- // Since each widget component manages its own state via ngModel,
412
- // we need to query the DOM to get the current values
413
- const fields = this.formWidget.fields || [];
414
-
415
- fields.forEach((field: any) => {
416
- const fieldId = this.getFieldId(field.name);
417
- const element = document.getElementById(fieldId) as HTMLInputElement | HTMLSelectElement | HTMLTextAreaElement;
418
-
419
- if (element) {
420
- if (element.type === 'checkbox') {
421
- this.formData[field.name] = (element as HTMLInputElement).checked;
422
- } else if (element.type === 'file') {
423
- this.formData[field.name] = (element as HTMLInputElement).files?.[0];
424
- } else if (element.tagName === 'SELECT' && (element as HTMLSelectElement).multiple) {
425
- const select = element as HTMLSelectElement;
426
- this.formData[field.name] = Array.from(select.selectedOptions).map(opt => opt.value);
427
- } else {
428
- this.formData[field.name] = element.value;
429
- }
430
- }
431
- });
432
- }
433
- }
@@ -1,33 +0,0 @@
1
- import { Component, Input } from '@angular/core';
2
- import { CommonModule } from '@angular/common';
3
- import type { ImageWidget } from '@bbq-chat/widgets';
4
- import { CustomWidgetComponent } from '../custom-widget-renderer.types';
5
-
6
- @Component({
7
- selector: 'bbq-image-widget',
8
- standalone: true,
9
- imports: [CommonModule],
10
- template: `
11
- <div
12
- class="bbq-widget bbq-image"
13
- [attr.data-widget-type]="'image'"
14
- [attr.data-action]="imageWidget.action">
15
- <img
16
- class="bbq-image-img"
17
- [src]="imageWidget.imageUrl"
18
- [alt]="imageWidget.alt || 'Image'"
19
- [style.width]="imageWidget.width ? imageWidget.width + 'px' : 'auto'"
20
- [style.height]="imageWidget.height ? imageWidget.height + 'px' : 'auto'"
21
- loading="lazy" />
22
- </div>
23
- `,
24
- styles: []
25
- })
26
- export class ImageWidgetComponent implements CustomWidgetComponent {
27
- @Input() widget!: any;
28
- widgetAction?: (actionName: string, payload: unknown) => void;
29
-
30
- get imageWidget(): ImageWidget {
31
- return this.widget as ImageWidget;
32
- }
33
- }
@@ -1,39 +0,0 @@
1
- import { Component, Input } from '@angular/core';
2
- import { CommonModule } from '@angular/common';
3
- import type { ImageCollectionWidget } from '@bbq-chat/widgets';
4
- import { CustomWidgetComponent } from '../custom-widget-renderer.types';
5
-
6
- @Component({
7
- selector: 'bbq-imagecollection-widget',
8
- standalone: true,
9
- imports: [CommonModule],
10
- template: `
11
- <div
12
- class="bbq-widget bbq-image-collection"
13
- [attr.data-widget-type]="'imagecollection'"
14
- [attr.data-action]="imageCollectionWidget.action">
15
- <div class="bbq-image-collection-grid">
16
- @for (image of imageCollectionWidget.images; track image.imageUrl) {
17
- <div class="bbq-image-collection-item">
18
- <img
19
- class="bbq-image-collection-img"
20
- [src]="image.imageUrl"
21
- [alt]="image.alt || 'Image'"
22
- [style.width]="image.width ? image.width + 'px' : 'auto'"
23
- [style.height]="image.height ? image.height + 'px' : 'auto'"
24
- loading="lazy" />
25
- </div>
26
- }
27
- </div>
28
- </div>
29
- `,
30
- styles: []
31
- })
32
- export class ImageCollectionWidgetComponent implements CustomWidgetComponent {
33
- @Input() widget!: any;
34
- widgetAction?: (actionName: string, payload: unknown) => void;
35
-
36
- get imageCollectionWidget(): ImageCollectionWidget {
37
- return this.widget as ImageCollectionWidget;
38
- }
39
- }
@@ -1,20 +0,0 @@
1
- /**
2
- * Built-in widget components
3
- * These components provide Angular implementations for all standard widget types
4
- */
5
-
6
- export { ButtonWidgetComponent } from './button.component';
7
- export { CardWidgetComponent } from './card.component';
8
- export { InputWidgetComponent } from './input.component';
9
- export { TextAreaWidgetComponent } from './textarea.component';
10
- export { DropdownWidgetComponent } from './dropdown.component';
11
- export { SliderWidgetComponent } from './slider.component';
12
- export { ToggleWidgetComponent } from './toggle.component';
13
- export { FileUploadWidgetComponent } from './fileupload.component';
14
- export { ThemeSwitcherWidgetComponent } from './themeswitcher.component';
15
- export { DatePickerWidgetComponent } from './datepicker.component';
16
- export { MultiSelectWidgetComponent } from './multiselect.component';
17
- export { ProgressBarWidgetComponent } from './progressbar.component';
18
- export { FormWidgetComponent } from './form.component';
19
- export { ImageWidgetComponent } from './image.component';
20
- export { ImageCollectionWidgetComponent } from './imagecollection.component';
@@ -1,63 +0,0 @@
1
- import { Component, Input, OnInit } from '@angular/core';
2
- import { CommonModule } from '@angular/common';
3
- import { FormsModule } from '@angular/forms';
4
- import type { InputWidget } from '@bbq-chat/widgets';
5
- import { CustomWidgetComponent } from '../custom-widget-renderer.types';
6
-
7
- @Component({
8
- selector: 'bbq-input-widget',
9
- standalone: true,
10
- imports: [CommonModule, FormsModule],
11
- template: `
12
- <div
13
- class="bbq-widget bbq-input"
14
- [attr.data-widget-type]="'input'">
15
- <label *ngIf="showLabel" class="bbq-input-label" [attr.for]="inputId">
16
- {{ inputWidget.label }}
17
- </label>
18
- <input
19
- type="text"
20
- [id]="inputId"
21
- [ngClass]="inputClasses"
22
- [attr.data-action]="inputWidget.action"
23
- [placeholder]="inputWidget.placeholder || ''"
24
- [maxLength]="inputWidget.maxLength || 0"
25
- [(ngModel)]="value" />
26
- </div>
27
- `,
28
- styles: []
29
- })
30
- export class InputWidgetComponent implements CustomWidgetComponent, OnInit {
31
- @Input() widget!: any;
32
- widgetAction?: (actionName: string, payload: unknown) => void;
33
-
34
- value = '';
35
- inputId = '';
36
-
37
- get inputWidget(): InputWidget {
38
- return this.widget as InputWidget;
39
- }
40
-
41
- get showLabel(): boolean {
42
- const widget = this.inputWidget as any;
43
- if (widget.hideLabel === true) {
44
- return false;
45
- }
46
- if (widget.showLabel === false) {
47
- return false;
48
- }
49
- return true;
50
- }
51
-
52
- get inputClasses(): string[] {
53
- return this.isFormAppearance ? ['bbq-form-input'] : ['bbq-input'];
54
- }
55
-
56
- private get isFormAppearance(): boolean {
57
- return (this.inputWidget as any).appearance === 'form';
58
- }
59
-
60
- ngOnInit() {
61
- this.inputId = `bbq-${this.inputWidget.action.replace(/\s+/g, '-').toLowerCase()}-input`;
62
- }
63
- }
@@ -1,67 +0,0 @@
1
- import { Component, Input, OnInit } from '@angular/core';
2
- import { CommonModule } from '@angular/common';
3
- import { FormsModule } from '@angular/forms';
4
- import type { MultiSelectWidget } from '@bbq-chat/widgets';
5
- import { CustomWidgetComponent } from '../custom-widget-renderer.types';
6
-
7
- @Component({
8
- selector: 'bbq-multiselect-widget',
9
- standalone: true,
10
- imports: [CommonModule, FormsModule],
11
- template: `
12
- <div
13
- class="bbq-widget bbq-multi-select"
14
- [attr.data-widget-type]="'multiselect'">
15
- <label *ngIf="showLabel" class="bbq-multi-select-label" [attr.for]="selectId">
16
- {{ multiSelectWidget.label }}
17
- </label>
18
- <select
19
- [id]="selectId"
20
- [ngClass]="selectClasses"
21
- [attr.data-action]="multiSelectWidget.action"
22
- multiple
23
- [(ngModel)]="values">
24
- @for (option of multiSelectWidget.options; track option) {
25
- <option [value]="option">{{ option }}</option>
26
- }
27
- </select>
28
- </div>
29
- `,
30
- styles: []
31
- })
32
- export class MultiSelectWidgetComponent implements CustomWidgetComponent, OnInit {
33
- @Input() widget!: any;
34
- widgetAction?: (actionName: string, payload: unknown) => void;
35
-
36
- values: string[] = [];
37
- selectId = '';
38
-
39
- get multiSelectWidget(): MultiSelectWidget {
40
- return this.widget as MultiSelectWidget;
41
- }
42
-
43
- get showLabel(): boolean {
44
- const widget = this.multiSelectWidget as any;
45
- if (widget.hideLabel === true) {
46
- return false;
47
- }
48
- if (widget.showLabel === false) {
49
- return false;
50
- }
51
- return true;
52
- }
53
-
54
- get selectClasses(): string[] {
55
- return this.isFormAppearance
56
- ? ['bbq-form-multiselect', 'bbq-form-select']
57
- : ['bbq-form-multiselect', 'bbq-form-select'];
58
- }
59
-
60
- private get isFormAppearance(): boolean {
61
- return (this.multiSelectWidget as any).appearance === 'form';
62
- }
63
-
64
- ngOnInit() {
65
- this.selectId = `bbq-${this.multiSelectWidget.action.replace(/\s+/g, '-').toLowerCase()}-select`;
66
- }
67
- }