@bbq-chat/widgets-angular 1.0.3 → 1.0.5
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/README.md +92 -3
- package/dist/README.md +341 -0
- package/dist/fesm2022/index.mjs +1992 -0
- package/dist/fesm2022/index.mjs.map +1 -0
- package/dist/types/index.d.ts +618 -0
- package/package.json +18 -15
|
@@ -0,0 +1,1992 @@
|
|
|
1
|
+
import * as i0 from '@angular/core';
|
|
2
|
+
import { Input, Component, createComponent, ViewContainerRef, ViewChildren, InjectionToken, Injectable, EventEmitter, ViewChild, Output, Inject, Optional } from '@angular/core';
|
|
3
|
+
import * as i1 from '@angular/common';
|
|
4
|
+
import { CommonModule } from '@angular/common';
|
|
5
|
+
import * as i2$1 from '@bbq-chat/widgets';
|
|
6
|
+
import { WidgetEventManager, SsrWidgetRenderer, customWidgetRegistry } from '@bbq-chat/widgets';
|
|
7
|
+
export { ChatWidget, SsrWidgetRenderer, WidgetEventManager, customWidgetRegistry } from '@bbq-chat/widgets';
|
|
8
|
+
import * as i2 from '@angular/forms';
|
|
9
|
+
import { FormsModule } from '@angular/forms';
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Type guard to check if a renderer is a TemplateRef
|
|
13
|
+
*/
|
|
14
|
+
function isTemplateRenderer(renderer) {
|
|
15
|
+
return (renderer !== null &&
|
|
16
|
+
typeof renderer === 'object' &&
|
|
17
|
+
'createEmbeddedView' in renderer);
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Type guard to check if a renderer is an Angular Component
|
|
21
|
+
*
|
|
22
|
+
* Note: This uses a heuristic check based on the following assumptions:
|
|
23
|
+
* 1. Components are constructor functions
|
|
24
|
+
* 2. Components have a prototype with a constructor property
|
|
25
|
+
* 3. Components typically use dependency injection (no required constructor params)
|
|
26
|
+
*
|
|
27
|
+
* Limitation: This may not detect components with required constructor parameters.
|
|
28
|
+
* For edge cases, explicitly check your component's constructor signature.
|
|
29
|
+
*
|
|
30
|
+
* Alternative: You can always register a wrapper component that has no required params.
|
|
31
|
+
*/
|
|
32
|
+
function isComponentRenderer(renderer) {
|
|
33
|
+
// Check if it's a function (constructor) but not a regular function renderer
|
|
34
|
+
if (typeof renderer !== 'function') {
|
|
35
|
+
return false;
|
|
36
|
+
}
|
|
37
|
+
// Check for Angular component characteristics
|
|
38
|
+
// Components typically have prototype with constructor property
|
|
39
|
+
return (renderer.prototype !== undefined &&
|
|
40
|
+
renderer.prototype.constructor === renderer &&
|
|
41
|
+
renderer.length === 0 // Constructor with no required params (Angular DI)
|
|
42
|
+
);
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Type guard to check if a renderer is an HTML function
|
|
46
|
+
*
|
|
47
|
+
* Note: This should be checked AFTER checking for component and template renderers
|
|
48
|
+
* since components are also functions but with additional properties.
|
|
49
|
+
*/
|
|
50
|
+
function isHtmlRenderer(renderer) {
|
|
51
|
+
return typeof renderer === 'function';
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Angular widget renderer
|
|
56
|
+
* Returns Angular component types for dynamic rendering
|
|
57
|
+
* Provides feature parity with SsrWidgetRenderer but uses Angular components
|
|
58
|
+
*/
|
|
59
|
+
class AngularWidgetRenderer {
|
|
60
|
+
framework = 'Angular';
|
|
61
|
+
overrides;
|
|
62
|
+
componentRegistry = new Map();
|
|
63
|
+
constructor(options) {
|
|
64
|
+
this.overrides = options?.components;
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Register all built-in widget components
|
|
68
|
+
* Must be called after components are imported to avoid circular dependencies
|
|
69
|
+
*/
|
|
70
|
+
registerBuiltInComponents(components) {
|
|
71
|
+
for (const [type, component] of Object.entries(components)) {
|
|
72
|
+
this.componentRegistry.set(type, component);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Register or override a widget component
|
|
77
|
+
* Use this to replace built-in components or add custom ones
|
|
78
|
+
*
|
|
79
|
+
* @example
|
|
80
|
+
* ```typescript
|
|
81
|
+
* renderer.registerComponent('button', MyCustomButtonComponent);
|
|
82
|
+
* ```
|
|
83
|
+
*/
|
|
84
|
+
registerComponent(type, component) {
|
|
85
|
+
this.componentRegistry.set(type, component);
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* Register multiple widget components at once
|
|
89
|
+
*
|
|
90
|
+
* @example
|
|
91
|
+
* ```typescript
|
|
92
|
+
* renderer.registerComponents({
|
|
93
|
+
* button: MyButtonComponent,
|
|
94
|
+
* card: MyCardComponent
|
|
95
|
+
* });
|
|
96
|
+
* ```
|
|
97
|
+
*/
|
|
98
|
+
registerComponents(components) {
|
|
99
|
+
for (const [type, component] of Object.entries(components)) {
|
|
100
|
+
this.componentRegistry.set(type, component);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* Get the Angular component type for a given widget
|
|
105
|
+
* Returns the component class that should be dynamically instantiated
|
|
106
|
+
*/
|
|
107
|
+
getComponentType(widget) {
|
|
108
|
+
const type = widget.type;
|
|
109
|
+
// Check for custom override first
|
|
110
|
+
if (this.overrides && this.overrides[type]) {
|
|
111
|
+
return this.overrides[type];
|
|
112
|
+
}
|
|
113
|
+
// Check built-in registry
|
|
114
|
+
if (this.componentRegistry.has(type)) {
|
|
115
|
+
return this.componentRegistry.get(type);
|
|
116
|
+
}
|
|
117
|
+
return null;
|
|
118
|
+
}
|
|
119
|
+
/**
|
|
120
|
+
* Legacy method for IWidgetRenderer interface compatibility
|
|
121
|
+
* Not used in Angular rendering but required by interface
|
|
122
|
+
* @deprecated Use getComponentType() instead for Angular rendering
|
|
123
|
+
*/
|
|
124
|
+
renderWidget(widget) {
|
|
125
|
+
// This method is not used in Angular rendering
|
|
126
|
+
// It's only here for interface compatibility
|
|
127
|
+
return `<!-- Angular component rendering for ${widget.type} -->`;
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
class ButtonWidgetComponent {
|
|
132
|
+
widget;
|
|
133
|
+
widgetAction;
|
|
134
|
+
get buttonWidget() {
|
|
135
|
+
return this.widget;
|
|
136
|
+
}
|
|
137
|
+
onClick() {
|
|
138
|
+
if (this.widgetAction) {
|
|
139
|
+
this.widgetAction(this.buttonWidget.action, {});
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.8", ngImport: i0, type: ButtonWidgetComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
143
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "21.0.8", type: ButtonWidgetComponent, isStandalone: true, selector: "bbq-button-widget", inputs: { widget: "widget" }, ngImport: i0, template: `
|
|
144
|
+
<button
|
|
145
|
+
class="bbq-widget bbq-button"
|
|
146
|
+
[attr.data-widget-type]="'button'"
|
|
147
|
+
[attr.data-action]="buttonWidget.action"
|
|
148
|
+
type="button"
|
|
149
|
+
(click)="onClick()">
|
|
150
|
+
{{ buttonWidget.label }}
|
|
151
|
+
</button>
|
|
152
|
+
`, isInline: true, dependencies: [{ kind: "ngmodule", type: CommonModule }] });
|
|
153
|
+
}
|
|
154
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.8", ngImport: i0, type: ButtonWidgetComponent, decorators: [{
|
|
155
|
+
type: Component,
|
|
156
|
+
args: [{ selector: 'bbq-button-widget', standalone: true, imports: [CommonModule], template: `
|
|
157
|
+
<button
|
|
158
|
+
class="bbq-widget bbq-button"
|
|
159
|
+
[attr.data-widget-type]="'button'"
|
|
160
|
+
[attr.data-action]="buttonWidget.action"
|
|
161
|
+
type="button"
|
|
162
|
+
(click)="onClick()">
|
|
163
|
+
{{ buttonWidget.label }}
|
|
164
|
+
</button>
|
|
165
|
+
` }]
|
|
166
|
+
}], propDecorators: { widget: [{
|
|
167
|
+
type: Input
|
|
168
|
+
}] } });
|
|
169
|
+
|
|
170
|
+
class CardWidgetComponent {
|
|
171
|
+
widget;
|
|
172
|
+
widgetAction;
|
|
173
|
+
get cardWidget() {
|
|
174
|
+
return this.widget;
|
|
175
|
+
}
|
|
176
|
+
onClick() {
|
|
177
|
+
if (this.widgetAction) {
|
|
178
|
+
this.widgetAction(this.cardWidget.action, {});
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.8", ngImport: i0, type: CardWidgetComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
182
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.8", type: CardWidgetComponent, isStandalone: true, selector: "bbq-card-widget", inputs: { widget: "widget" }, ngImport: i0, template: `
|
|
183
|
+
<div
|
|
184
|
+
class="bbq-widget bbq-card"
|
|
185
|
+
[attr.data-widget-type]="'card'"
|
|
186
|
+
[attr.data-action]="cardWidget.action"
|
|
187
|
+
role="article">
|
|
188
|
+
<h3 class="bbq-card-title">{{ cardWidget.title }}</h3>
|
|
189
|
+
@if (cardWidget.description) {
|
|
190
|
+
<p class="bbq-card-description">{{ cardWidget.description }}</p>
|
|
191
|
+
}
|
|
192
|
+
@if (cardWidget.imageUrl) {
|
|
193
|
+
<img
|
|
194
|
+
class="bbq-card-image"
|
|
195
|
+
[src]="cardWidget.imageUrl"
|
|
196
|
+
[alt]="cardWidget.title"
|
|
197
|
+
loading="lazy"
|
|
198
|
+
style="display:block;max-width:100%;height:auto;object-fit:cover;max-height:200px;border-radius:6px;margin-bottom:12px;" />
|
|
199
|
+
}
|
|
200
|
+
<button
|
|
201
|
+
class="bbq-card-action bbq-button"
|
|
202
|
+
[attr.data-action]="cardWidget.action"
|
|
203
|
+
type="button"
|
|
204
|
+
(click)="onClick()">
|
|
205
|
+
{{ cardWidget.label }}
|
|
206
|
+
</button>
|
|
207
|
+
</div>
|
|
208
|
+
`, isInline: true, dependencies: [{ kind: "ngmodule", type: CommonModule }] });
|
|
209
|
+
}
|
|
210
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.8", ngImport: i0, type: CardWidgetComponent, decorators: [{
|
|
211
|
+
type: Component,
|
|
212
|
+
args: [{ selector: 'bbq-card-widget', standalone: true, imports: [CommonModule], template: `
|
|
213
|
+
<div
|
|
214
|
+
class="bbq-widget bbq-card"
|
|
215
|
+
[attr.data-widget-type]="'card'"
|
|
216
|
+
[attr.data-action]="cardWidget.action"
|
|
217
|
+
role="article">
|
|
218
|
+
<h3 class="bbq-card-title">{{ cardWidget.title }}</h3>
|
|
219
|
+
@if (cardWidget.description) {
|
|
220
|
+
<p class="bbq-card-description">{{ cardWidget.description }}</p>
|
|
221
|
+
}
|
|
222
|
+
@if (cardWidget.imageUrl) {
|
|
223
|
+
<img
|
|
224
|
+
class="bbq-card-image"
|
|
225
|
+
[src]="cardWidget.imageUrl"
|
|
226
|
+
[alt]="cardWidget.title"
|
|
227
|
+
loading="lazy"
|
|
228
|
+
style="display:block;max-width:100%;height:auto;object-fit:cover;max-height:200px;border-radius:6px;margin-bottom:12px;" />
|
|
229
|
+
}
|
|
230
|
+
<button
|
|
231
|
+
class="bbq-card-action bbq-button"
|
|
232
|
+
[attr.data-action]="cardWidget.action"
|
|
233
|
+
type="button"
|
|
234
|
+
(click)="onClick()">
|
|
235
|
+
{{ cardWidget.label }}
|
|
236
|
+
</button>
|
|
237
|
+
</div>
|
|
238
|
+
` }]
|
|
239
|
+
}], propDecorators: { widget: [{
|
|
240
|
+
type: Input
|
|
241
|
+
}] } });
|
|
242
|
+
|
|
243
|
+
class InputWidgetComponent {
|
|
244
|
+
widget;
|
|
245
|
+
widgetAction;
|
|
246
|
+
value = '';
|
|
247
|
+
inputId = '';
|
|
248
|
+
get inputWidget() {
|
|
249
|
+
return this.widget;
|
|
250
|
+
}
|
|
251
|
+
get showLabel() {
|
|
252
|
+
const widget = this.inputWidget;
|
|
253
|
+
if (widget.hideLabel === true) {
|
|
254
|
+
return false;
|
|
255
|
+
}
|
|
256
|
+
if (widget.showLabel === false) {
|
|
257
|
+
return false;
|
|
258
|
+
}
|
|
259
|
+
return true;
|
|
260
|
+
}
|
|
261
|
+
get inputClasses() {
|
|
262
|
+
return this.isFormAppearance ? ['bbq-form-input'] : ['bbq-input'];
|
|
263
|
+
}
|
|
264
|
+
get isFormAppearance() {
|
|
265
|
+
return this.inputWidget.appearance === 'form';
|
|
266
|
+
}
|
|
267
|
+
ngOnInit() {
|
|
268
|
+
this.inputId = `bbq-${this.inputWidget.action.replace(/\s+/g, '-').toLowerCase()}-input`;
|
|
269
|
+
}
|
|
270
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.8", ngImport: i0, type: InputWidgetComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
271
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "21.0.8", type: InputWidgetComponent, isStandalone: true, selector: "bbq-input-widget", inputs: { widget: "widget" }, ngImport: i0, template: `
|
|
272
|
+
<div
|
|
273
|
+
class="bbq-widget bbq-input"
|
|
274
|
+
[attr.data-widget-type]="'input'">
|
|
275
|
+
<label *ngIf="showLabel" class="bbq-input-label" [attr.for]="inputId">
|
|
276
|
+
{{ inputWidget.label }}
|
|
277
|
+
</label>
|
|
278
|
+
<input
|
|
279
|
+
type="text"
|
|
280
|
+
[id]="inputId"
|
|
281
|
+
[ngClass]="inputClasses"
|
|
282
|
+
[attr.data-action]="inputWidget.action"
|
|
283
|
+
[placeholder]="inputWidget.placeholder || ''"
|
|
284
|
+
[maxLength]="inputWidget.maxLength || 0"
|
|
285
|
+
[(ngModel)]="value" />
|
|
286
|
+
</div>
|
|
287
|
+
`, isInline: true, dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i2.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }] });
|
|
288
|
+
}
|
|
289
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.8", ngImport: i0, type: InputWidgetComponent, decorators: [{
|
|
290
|
+
type: Component,
|
|
291
|
+
args: [{ selector: 'bbq-input-widget', standalone: true, imports: [CommonModule, FormsModule], template: `
|
|
292
|
+
<div
|
|
293
|
+
class="bbq-widget bbq-input"
|
|
294
|
+
[attr.data-widget-type]="'input'">
|
|
295
|
+
<label *ngIf="showLabel" class="bbq-input-label" [attr.for]="inputId">
|
|
296
|
+
{{ inputWidget.label }}
|
|
297
|
+
</label>
|
|
298
|
+
<input
|
|
299
|
+
type="text"
|
|
300
|
+
[id]="inputId"
|
|
301
|
+
[ngClass]="inputClasses"
|
|
302
|
+
[attr.data-action]="inputWidget.action"
|
|
303
|
+
[placeholder]="inputWidget.placeholder || ''"
|
|
304
|
+
[maxLength]="inputWidget.maxLength || 0"
|
|
305
|
+
[(ngModel)]="value" />
|
|
306
|
+
</div>
|
|
307
|
+
` }]
|
|
308
|
+
}], propDecorators: { widget: [{
|
|
309
|
+
type: Input
|
|
310
|
+
}] } });
|
|
311
|
+
|
|
312
|
+
class TextAreaWidgetComponent {
|
|
313
|
+
widget;
|
|
314
|
+
widgetAction;
|
|
315
|
+
value = '';
|
|
316
|
+
textareaId = '';
|
|
317
|
+
get textareaWidget() {
|
|
318
|
+
return this.widget;
|
|
319
|
+
}
|
|
320
|
+
get showLabel() {
|
|
321
|
+
const widget = this.textareaWidget;
|
|
322
|
+
if (widget.hideLabel === true) {
|
|
323
|
+
return false;
|
|
324
|
+
}
|
|
325
|
+
if (widget.showLabel === false) {
|
|
326
|
+
return false;
|
|
327
|
+
}
|
|
328
|
+
return true;
|
|
329
|
+
}
|
|
330
|
+
get textareaClasses() {
|
|
331
|
+
return this.isFormAppearance ? ['bbq-form-textarea'] : ['bbq-form-textarea', 'bbq-input'];
|
|
332
|
+
}
|
|
333
|
+
get isFormAppearance() {
|
|
334
|
+
return this.textareaWidget.appearance === 'form';
|
|
335
|
+
}
|
|
336
|
+
ngOnInit() {
|
|
337
|
+
this.textareaId = `bbq-${this.textareaWidget.action.replace(/\s+/g, '-').toLowerCase()}-textarea`;
|
|
338
|
+
}
|
|
339
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.8", ngImport: i0, type: TextAreaWidgetComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
340
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "21.0.8", type: TextAreaWidgetComponent, isStandalone: true, selector: "bbq-textarea-widget", inputs: { widget: "widget" }, ngImport: i0, template: `
|
|
341
|
+
<div
|
|
342
|
+
class="bbq-widget bbq-textarea"
|
|
343
|
+
[attr.data-widget-type]="'textarea'">
|
|
344
|
+
<label *ngIf="showLabel" class="bbq-textarea-label" [attr.for]="textareaId">
|
|
345
|
+
{{ textareaWidget.label }}
|
|
346
|
+
</label>
|
|
347
|
+
<textarea
|
|
348
|
+
[id]="textareaId"
|
|
349
|
+
[ngClass]="textareaClasses"
|
|
350
|
+
[attr.data-action]="textareaWidget.action"
|
|
351
|
+
[placeholder]="textareaWidget.placeholder || ''"
|
|
352
|
+
[maxLength]="textareaWidget.maxLength || 0"
|
|
353
|
+
[rows]="textareaWidget.rows || 4"
|
|
354
|
+
[(ngModel)]="value"></textarea>
|
|
355
|
+
</div>
|
|
356
|
+
`, isInline: true, dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i2.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }] });
|
|
357
|
+
}
|
|
358
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.8", ngImport: i0, type: TextAreaWidgetComponent, decorators: [{
|
|
359
|
+
type: Component,
|
|
360
|
+
args: [{ selector: 'bbq-textarea-widget', standalone: true, imports: [CommonModule, FormsModule], template: `
|
|
361
|
+
<div
|
|
362
|
+
class="bbq-widget bbq-textarea"
|
|
363
|
+
[attr.data-widget-type]="'textarea'">
|
|
364
|
+
<label *ngIf="showLabel" class="bbq-textarea-label" [attr.for]="textareaId">
|
|
365
|
+
{{ textareaWidget.label }}
|
|
366
|
+
</label>
|
|
367
|
+
<textarea
|
|
368
|
+
[id]="textareaId"
|
|
369
|
+
[ngClass]="textareaClasses"
|
|
370
|
+
[attr.data-action]="textareaWidget.action"
|
|
371
|
+
[placeholder]="textareaWidget.placeholder || ''"
|
|
372
|
+
[maxLength]="textareaWidget.maxLength || 0"
|
|
373
|
+
[rows]="textareaWidget.rows || 4"
|
|
374
|
+
[(ngModel)]="value"></textarea>
|
|
375
|
+
</div>
|
|
376
|
+
` }]
|
|
377
|
+
}], propDecorators: { widget: [{
|
|
378
|
+
type: Input
|
|
379
|
+
}] } });
|
|
380
|
+
|
|
381
|
+
class DropdownWidgetComponent {
|
|
382
|
+
widget;
|
|
383
|
+
widgetAction;
|
|
384
|
+
value = '';
|
|
385
|
+
selectId = '';
|
|
386
|
+
get dropdownWidget() {
|
|
387
|
+
return this.widget;
|
|
388
|
+
}
|
|
389
|
+
get showLabel() {
|
|
390
|
+
const widget = this.dropdownWidget;
|
|
391
|
+
if (widget.hideLabel === true) {
|
|
392
|
+
return false;
|
|
393
|
+
}
|
|
394
|
+
if (widget.showLabel === false) {
|
|
395
|
+
return false;
|
|
396
|
+
}
|
|
397
|
+
return true;
|
|
398
|
+
}
|
|
399
|
+
get selectClasses() {
|
|
400
|
+
return this.isFormAppearance ? ['bbq-form-select'] : ['bbq-dropdown'];
|
|
401
|
+
}
|
|
402
|
+
get isFormAppearance() {
|
|
403
|
+
return this.dropdownWidget.appearance === 'form';
|
|
404
|
+
}
|
|
405
|
+
ngOnInit() {
|
|
406
|
+
this.selectId = `bbq-${this.dropdownWidget.action.replace(/\s+/g, '-').toLowerCase()}-select`;
|
|
407
|
+
this.value = this.dropdownWidget.options[0] || '';
|
|
408
|
+
}
|
|
409
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.8", ngImport: i0, type: DropdownWidgetComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
410
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.8", type: DropdownWidgetComponent, isStandalone: true, selector: "bbq-dropdown-widget", inputs: { widget: "widget" }, ngImport: i0, template: `
|
|
411
|
+
<div
|
|
412
|
+
class="bbq-widget bbq-dropdown"
|
|
413
|
+
[attr.data-widget-type]="'dropdown'">
|
|
414
|
+
<label *ngIf="showLabel" class="bbq-dropdown-label" [attr.for]="selectId">
|
|
415
|
+
{{ dropdownWidget.label }}
|
|
416
|
+
</label>
|
|
417
|
+
<select
|
|
418
|
+
[id]="selectId"
|
|
419
|
+
[ngClass]="selectClasses"
|
|
420
|
+
[attr.data-action]="dropdownWidget.action"
|
|
421
|
+
[(ngModel)]="value">
|
|
422
|
+
@for (option of dropdownWidget.options; track option) {
|
|
423
|
+
<option [value]="option">{{ option }}</option>
|
|
424
|
+
}
|
|
425
|
+
</select>
|
|
426
|
+
</div>
|
|
427
|
+
`, isInline: true, dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i2.NgSelectOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i2.ɵNgSelectMultipleOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i2.SelectControlValueAccessor, selector: "select:not([multiple])[formControlName],select:not([multiple])[formControl],select:not([multiple])[ngModel]", inputs: ["compareWith"] }, { kind: "directive", type: i2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }] });
|
|
428
|
+
}
|
|
429
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.8", ngImport: i0, type: DropdownWidgetComponent, decorators: [{
|
|
430
|
+
type: Component,
|
|
431
|
+
args: [{ selector: 'bbq-dropdown-widget', standalone: true, imports: [CommonModule, FormsModule], template: `
|
|
432
|
+
<div
|
|
433
|
+
class="bbq-widget bbq-dropdown"
|
|
434
|
+
[attr.data-widget-type]="'dropdown'">
|
|
435
|
+
<label *ngIf="showLabel" class="bbq-dropdown-label" [attr.for]="selectId">
|
|
436
|
+
{{ dropdownWidget.label }}
|
|
437
|
+
</label>
|
|
438
|
+
<select
|
|
439
|
+
[id]="selectId"
|
|
440
|
+
[ngClass]="selectClasses"
|
|
441
|
+
[attr.data-action]="dropdownWidget.action"
|
|
442
|
+
[(ngModel)]="value">
|
|
443
|
+
@for (option of dropdownWidget.options; track option) {
|
|
444
|
+
<option [value]="option">{{ option }}</option>
|
|
445
|
+
}
|
|
446
|
+
</select>
|
|
447
|
+
</div>
|
|
448
|
+
` }]
|
|
449
|
+
}], propDecorators: { widget: [{
|
|
450
|
+
type: Input
|
|
451
|
+
}] } });
|
|
452
|
+
|
|
453
|
+
class SliderWidgetComponent {
|
|
454
|
+
widget;
|
|
455
|
+
widgetAction;
|
|
456
|
+
value = 0;
|
|
457
|
+
sliderId = '';
|
|
458
|
+
get sliderWidget() {
|
|
459
|
+
return this.widget;
|
|
460
|
+
}
|
|
461
|
+
get showLabel() {
|
|
462
|
+
const widget = this.sliderWidget;
|
|
463
|
+
if (widget.hideLabel === true) {
|
|
464
|
+
return false;
|
|
465
|
+
}
|
|
466
|
+
if (widget.showLabel === false) {
|
|
467
|
+
return false;
|
|
468
|
+
}
|
|
469
|
+
return true;
|
|
470
|
+
}
|
|
471
|
+
get sliderClasses() {
|
|
472
|
+
return this.isFormAppearance ? ['bbq-form-slider'] : ['bbq-slider'];
|
|
473
|
+
}
|
|
474
|
+
get isFormAppearance() {
|
|
475
|
+
return this.sliderWidget.appearance === 'form';
|
|
476
|
+
}
|
|
477
|
+
ngOnInit() {
|
|
478
|
+
this.sliderId = `bbq-${this.sliderWidget.action.replace(/\s+/g, '-').toLowerCase()}-slider`;
|
|
479
|
+
this.value = this.sliderWidget.defaultValue ?? this.sliderWidget.min;
|
|
480
|
+
}
|
|
481
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.8", ngImport: i0, type: SliderWidgetComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
482
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "21.0.8", type: SliderWidgetComponent, isStandalone: true, selector: "bbq-slider-widget", inputs: { widget: "widget" }, ngImport: i0, template: `
|
|
483
|
+
<div
|
|
484
|
+
class="bbq-widget bbq-slider"
|
|
485
|
+
[attr.data-widget-type]="'slider'">
|
|
486
|
+
<label *ngIf="showLabel" class="bbq-slider-label" [attr.for]="sliderId">
|
|
487
|
+
{{ sliderWidget.label }}
|
|
488
|
+
</label>
|
|
489
|
+
<input
|
|
490
|
+
type="range"
|
|
491
|
+
[id]="sliderId"
|
|
492
|
+
[ngClass]="sliderClasses"
|
|
493
|
+
[min]="sliderWidget.min"
|
|
494
|
+
[max]="sliderWidget.max"
|
|
495
|
+
[step]="sliderWidget.step"
|
|
496
|
+
[attr.data-action]="sliderWidget.action"
|
|
497
|
+
[attr.aria-label]="sliderWidget.label"
|
|
498
|
+
[(ngModel)]="value" />
|
|
499
|
+
<span class="bbq-slider-value" aria-live="polite">{{ value }}</span>
|
|
500
|
+
</div>
|
|
501
|
+
`, isInline: true, dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i2.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i2.RangeValueAccessor, selector: "input[type=range][formControlName],input[type=range][formControl],input[type=range][ngModel]" }, { kind: "directive", type: i2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }] });
|
|
502
|
+
}
|
|
503
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.8", ngImport: i0, type: SliderWidgetComponent, decorators: [{
|
|
504
|
+
type: Component,
|
|
505
|
+
args: [{ selector: 'bbq-slider-widget', standalone: true, imports: [CommonModule, FormsModule], template: `
|
|
506
|
+
<div
|
|
507
|
+
class="bbq-widget bbq-slider"
|
|
508
|
+
[attr.data-widget-type]="'slider'">
|
|
509
|
+
<label *ngIf="showLabel" class="bbq-slider-label" [attr.for]="sliderId">
|
|
510
|
+
{{ sliderWidget.label }}
|
|
511
|
+
</label>
|
|
512
|
+
<input
|
|
513
|
+
type="range"
|
|
514
|
+
[id]="sliderId"
|
|
515
|
+
[ngClass]="sliderClasses"
|
|
516
|
+
[min]="sliderWidget.min"
|
|
517
|
+
[max]="sliderWidget.max"
|
|
518
|
+
[step]="sliderWidget.step"
|
|
519
|
+
[attr.data-action]="sliderWidget.action"
|
|
520
|
+
[attr.aria-label]="sliderWidget.label"
|
|
521
|
+
[(ngModel)]="value" />
|
|
522
|
+
<span class="bbq-slider-value" aria-live="polite">{{ value }}</span>
|
|
523
|
+
</div>
|
|
524
|
+
` }]
|
|
525
|
+
}], propDecorators: { widget: [{
|
|
526
|
+
type: Input
|
|
527
|
+
}] } });
|
|
528
|
+
|
|
529
|
+
class ToggleWidgetComponent {
|
|
530
|
+
widget;
|
|
531
|
+
widgetAction;
|
|
532
|
+
checked = false;
|
|
533
|
+
checkboxId = '';
|
|
534
|
+
get toggleWidget() {
|
|
535
|
+
return this.widget;
|
|
536
|
+
}
|
|
537
|
+
get showLabel() {
|
|
538
|
+
const widget = this.toggleWidget;
|
|
539
|
+
if (widget.hideLabel === true) {
|
|
540
|
+
return false;
|
|
541
|
+
}
|
|
542
|
+
if (widget.showLabel === false) {
|
|
543
|
+
return false;
|
|
544
|
+
}
|
|
545
|
+
return true;
|
|
546
|
+
}
|
|
547
|
+
get checkboxClasses() {
|
|
548
|
+
return this.isFormAppearance ? ['bbq-toggle-input', 'bbq-form-toggle'] : ['bbq-toggle-input'];
|
|
549
|
+
}
|
|
550
|
+
get isFormAppearance() {
|
|
551
|
+
return this.toggleWidget.appearance === 'form';
|
|
552
|
+
}
|
|
553
|
+
ngOnInit() {
|
|
554
|
+
this.checkboxId = `bbq-${this.toggleWidget.action.replace(/\s+/g, '-').toLowerCase()}-checkbox`;
|
|
555
|
+
this.checked = this.toggleWidget.defaultValue ?? false;
|
|
556
|
+
}
|
|
557
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.8", ngImport: i0, type: ToggleWidgetComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
558
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "21.0.8", type: ToggleWidgetComponent, isStandalone: true, selector: "bbq-toggle-widget", inputs: { widget: "widget" }, ngImport: i0, template: `
|
|
559
|
+
<div
|
|
560
|
+
class="bbq-widget bbq-toggle"
|
|
561
|
+
[attr.data-widget-type]="'toggle'">
|
|
562
|
+
<label class="bbq-toggle-label" [attr.for]="checkboxId">
|
|
563
|
+
<input
|
|
564
|
+
type="checkbox"
|
|
565
|
+
[id]="checkboxId"
|
|
566
|
+
[ngClass]="checkboxClasses"
|
|
567
|
+
[attr.data-action]="toggleWidget.action"
|
|
568
|
+
[attr.aria-label]="toggleWidget.label"
|
|
569
|
+
[(ngModel)]="checked" />
|
|
570
|
+
<span *ngIf="showLabel" class="bbq-toggle-text">{{ toggleWidget.label }}</span>
|
|
571
|
+
</label>
|
|
572
|
+
</div>
|
|
573
|
+
`, isInline: true, dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i2.CheckboxControlValueAccessor, selector: "input[type=checkbox][formControlName],input[type=checkbox][formControl],input[type=checkbox][ngModel]" }, { kind: "directive", type: i2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }] });
|
|
574
|
+
}
|
|
575
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.8", ngImport: i0, type: ToggleWidgetComponent, decorators: [{
|
|
576
|
+
type: Component,
|
|
577
|
+
args: [{ selector: 'bbq-toggle-widget', standalone: true, imports: [CommonModule, FormsModule], template: `
|
|
578
|
+
<div
|
|
579
|
+
class="bbq-widget bbq-toggle"
|
|
580
|
+
[attr.data-widget-type]="'toggle'">
|
|
581
|
+
<label class="bbq-toggle-label" [attr.for]="checkboxId">
|
|
582
|
+
<input
|
|
583
|
+
type="checkbox"
|
|
584
|
+
[id]="checkboxId"
|
|
585
|
+
[ngClass]="checkboxClasses"
|
|
586
|
+
[attr.data-action]="toggleWidget.action"
|
|
587
|
+
[attr.aria-label]="toggleWidget.label"
|
|
588
|
+
[(ngModel)]="checked" />
|
|
589
|
+
<span *ngIf="showLabel" class="bbq-toggle-text">{{ toggleWidget.label }}</span>
|
|
590
|
+
</label>
|
|
591
|
+
</div>
|
|
592
|
+
` }]
|
|
593
|
+
}], propDecorators: { widget: [{
|
|
594
|
+
type: Input
|
|
595
|
+
}] } });
|
|
596
|
+
|
|
597
|
+
class FileUploadWidgetComponent {
|
|
598
|
+
widget;
|
|
599
|
+
widgetAction;
|
|
600
|
+
inputId = '';
|
|
601
|
+
get fileUploadWidget() {
|
|
602
|
+
return this.widget;
|
|
603
|
+
}
|
|
604
|
+
get showLabel() {
|
|
605
|
+
const widget = this.fileUploadWidget;
|
|
606
|
+
if (widget.hideLabel === true) {
|
|
607
|
+
return false;
|
|
608
|
+
}
|
|
609
|
+
if (widget.showLabel === false) {
|
|
610
|
+
return false;
|
|
611
|
+
}
|
|
612
|
+
return true;
|
|
613
|
+
}
|
|
614
|
+
get inputClasses() {
|
|
615
|
+
return this.isFormAppearance ? ['bbq-form-fileupload'] : ['bbq-file'];
|
|
616
|
+
}
|
|
617
|
+
get isFormAppearance() {
|
|
618
|
+
return this.fileUploadWidget.appearance === 'form';
|
|
619
|
+
}
|
|
620
|
+
ngOnInit() {
|
|
621
|
+
this.inputId = `bbq-${this.fileUploadWidget.action.replace(/\s+/g, '-').toLowerCase()}-file`;
|
|
622
|
+
}
|
|
623
|
+
onFileChange(event) {
|
|
624
|
+
const target = event.target;
|
|
625
|
+
if (target.files && target.files.length > 0) {
|
|
626
|
+
const file = target.files[0];
|
|
627
|
+
if (this.widgetAction) {
|
|
628
|
+
this.widgetAction(this.fileUploadWidget.action, { file });
|
|
629
|
+
}
|
|
630
|
+
}
|
|
631
|
+
}
|
|
632
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.8", ngImport: i0, type: FileUploadWidgetComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
633
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "21.0.8", type: FileUploadWidgetComponent, isStandalone: true, selector: "bbq-fileupload-widget", inputs: { widget: "widget" }, ngImport: i0, template: `
|
|
634
|
+
<div
|
|
635
|
+
class="bbq-widget bbq-file-upload"
|
|
636
|
+
[attr.data-widget-type]="'fileupload'">
|
|
637
|
+
<label *ngIf="showLabel" class="bbq-file-label" [attr.for]="inputId">
|
|
638
|
+
{{ fileUploadWidget.label }}
|
|
639
|
+
</label>
|
|
640
|
+
<input
|
|
641
|
+
type="file"
|
|
642
|
+
[id]="inputId"
|
|
643
|
+
[ngClass]="inputClasses"
|
|
644
|
+
[attr.data-action]="fileUploadWidget.action"
|
|
645
|
+
[accept]="fileUploadWidget.accept || ''"
|
|
646
|
+
[attr.data-max-bytes]="fileUploadWidget.maxBytes"
|
|
647
|
+
(change)="onFileChange($event)" />
|
|
648
|
+
</div>
|
|
649
|
+
`, isInline: true, dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }] });
|
|
650
|
+
}
|
|
651
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.8", ngImport: i0, type: FileUploadWidgetComponent, decorators: [{
|
|
652
|
+
type: Component,
|
|
653
|
+
args: [{ selector: 'bbq-fileupload-widget', standalone: true, imports: [CommonModule], template: `
|
|
654
|
+
<div
|
|
655
|
+
class="bbq-widget bbq-file-upload"
|
|
656
|
+
[attr.data-widget-type]="'fileupload'">
|
|
657
|
+
<label *ngIf="showLabel" class="bbq-file-label" [attr.for]="inputId">
|
|
658
|
+
{{ fileUploadWidget.label }}
|
|
659
|
+
</label>
|
|
660
|
+
<input
|
|
661
|
+
type="file"
|
|
662
|
+
[id]="inputId"
|
|
663
|
+
[ngClass]="inputClasses"
|
|
664
|
+
[attr.data-action]="fileUploadWidget.action"
|
|
665
|
+
[accept]="fileUploadWidget.accept || ''"
|
|
666
|
+
[attr.data-max-bytes]="fileUploadWidget.maxBytes"
|
|
667
|
+
(change)="onFileChange($event)" />
|
|
668
|
+
</div>
|
|
669
|
+
` }]
|
|
670
|
+
}], propDecorators: { widget: [{
|
|
671
|
+
type: Input
|
|
672
|
+
}] } });
|
|
673
|
+
|
|
674
|
+
class ThemeSwitcherWidgetComponent {
|
|
675
|
+
widget;
|
|
676
|
+
widgetAction;
|
|
677
|
+
value = '';
|
|
678
|
+
selectId = '';
|
|
679
|
+
get themeSwitcherWidget() {
|
|
680
|
+
return this.widget;
|
|
681
|
+
}
|
|
682
|
+
ngOnInit() {
|
|
683
|
+
this.selectId = `bbq-${this.themeSwitcherWidget.action.replace(/\s+/g, '-').toLowerCase()}-select`;
|
|
684
|
+
this.value = this.themeSwitcherWidget.themes[0] || '';
|
|
685
|
+
}
|
|
686
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.8", ngImport: i0, type: ThemeSwitcherWidgetComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
687
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.8", type: ThemeSwitcherWidgetComponent, isStandalone: true, selector: "bbq-themeswitcher-widget", inputs: { widget: "widget" }, ngImport: i0, template: `
|
|
688
|
+
<div
|
|
689
|
+
class="bbq-widget bbq-theme-switcher"
|
|
690
|
+
[attr.data-widget-type]="'themeswitcher'">
|
|
691
|
+
<label class="bbq-theme-switcher-label" [attr.for]="selectId">
|
|
692
|
+
{{ themeSwitcherWidget.label }}
|
|
693
|
+
</label>
|
|
694
|
+
<select
|
|
695
|
+
[id]="selectId"
|
|
696
|
+
class="bbq-theme-switcher-select"
|
|
697
|
+
[attr.data-action]="themeSwitcherWidget.action"
|
|
698
|
+
[(ngModel)]="value">
|
|
699
|
+
@for (theme of themeSwitcherWidget.themes; track theme) {
|
|
700
|
+
<option [value]="theme">{{ theme }}</option>
|
|
701
|
+
}
|
|
702
|
+
</select>
|
|
703
|
+
</div>
|
|
704
|
+
`, isInline: true, dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i2.NgSelectOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i2.ɵNgSelectMultipleOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i2.SelectControlValueAccessor, selector: "select:not([multiple])[formControlName],select:not([multiple])[formControl],select:not([multiple])[ngModel]", inputs: ["compareWith"] }, { kind: "directive", type: i2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }] });
|
|
705
|
+
}
|
|
706
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.8", ngImport: i0, type: ThemeSwitcherWidgetComponent, decorators: [{
|
|
707
|
+
type: Component,
|
|
708
|
+
args: [{ selector: 'bbq-themeswitcher-widget', standalone: true, imports: [CommonModule, FormsModule], template: `
|
|
709
|
+
<div
|
|
710
|
+
class="bbq-widget bbq-theme-switcher"
|
|
711
|
+
[attr.data-widget-type]="'themeswitcher'">
|
|
712
|
+
<label class="bbq-theme-switcher-label" [attr.for]="selectId">
|
|
713
|
+
{{ themeSwitcherWidget.label }}
|
|
714
|
+
</label>
|
|
715
|
+
<select
|
|
716
|
+
[id]="selectId"
|
|
717
|
+
class="bbq-theme-switcher-select"
|
|
718
|
+
[attr.data-action]="themeSwitcherWidget.action"
|
|
719
|
+
[(ngModel)]="value">
|
|
720
|
+
@for (theme of themeSwitcherWidget.themes; track theme) {
|
|
721
|
+
<option [value]="theme">{{ theme }}</option>
|
|
722
|
+
}
|
|
723
|
+
</select>
|
|
724
|
+
</div>
|
|
725
|
+
` }]
|
|
726
|
+
}], propDecorators: { widget: [{
|
|
727
|
+
type: Input
|
|
728
|
+
}] } });
|
|
729
|
+
|
|
730
|
+
class DatePickerWidgetComponent {
|
|
731
|
+
widget;
|
|
732
|
+
widgetAction;
|
|
733
|
+
value = '';
|
|
734
|
+
inputId = '';
|
|
735
|
+
get datePickerWidget() {
|
|
736
|
+
return this.widget;
|
|
737
|
+
}
|
|
738
|
+
get showLabel() {
|
|
739
|
+
const widget = this.datePickerWidget;
|
|
740
|
+
if (widget.hideLabel === true) {
|
|
741
|
+
return false;
|
|
742
|
+
}
|
|
743
|
+
if (widget.showLabel === false) {
|
|
744
|
+
return false;
|
|
745
|
+
}
|
|
746
|
+
return true;
|
|
747
|
+
}
|
|
748
|
+
get inputClasses() {
|
|
749
|
+
return this.isFormAppearance ? ['bbq-form-datepicker'] : ['bbq-form-datepicker', 'bbq-input'];
|
|
750
|
+
}
|
|
751
|
+
get isFormAppearance() {
|
|
752
|
+
return this.datePickerWidget.appearance === 'form';
|
|
753
|
+
}
|
|
754
|
+
ngOnInit() {
|
|
755
|
+
this.inputId = `bbq-${this.datePickerWidget.action.replace(/\s+/g, '-').toLowerCase()}-date`;
|
|
756
|
+
}
|
|
757
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.8", ngImport: i0, type: DatePickerWidgetComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
758
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "21.0.8", type: DatePickerWidgetComponent, isStandalone: true, selector: "bbq-datepicker-widget", inputs: { widget: "widget" }, ngImport: i0, template: `
|
|
759
|
+
<div
|
|
760
|
+
class="bbq-widget bbq-date-picker"
|
|
761
|
+
[attr.data-widget-type]="'datepicker'">
|
|
762
|
+
<label *ngIf="showLabel" class="bbq-date-picker-label" [attr.for]="inputId">
|
|
763
|
+
{{ datePickerWidget.label }}
|
|
764
|
+
</label>
|
|
765
|
+
<input
|
|
766
|
+
type="date"
|
|
767
|
+
[id]="inputId"
|
|
768
|
+
[ngClass]="inputClasses"
|
|
769
|
+
[attr.data-action]="datePickerWidget.action"
|
|
770
|
+
[min]="datePickerWidget.minDate || ''"
|
|
771
|
+
[max]="datePickerWidget.maxDate || ''"
|
|
772
|
+
[(ngModel)]="value" />
|
|
773
|
+
</div>
|
|
774
|
+
`, isInline: true, dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i2.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }] });
|
|
775
|
+
}
|
|
776
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.8", ngImport: i0, type: DatePickerWidgetComponent, decorators: [{
|
|
777
|
+
type: Component,
|
|
778
|
+
args: [{ selector: 'bbq-datepicker-widget', standalone: true, imports: [CommonModule, FormsModule], template: `
|
|
779
|
+
<div
|
|
780
|
+
class="bbq-widget bbq-date-picker"
|
|
781
|
+
[attr.data-widget-type]="'datepicker'">
|
|
782
|
+
<label *ngIf="showLabel" class="bbq-date-picker-label" [attr.for]="inputId">
|
|
783
|
+
{{ datePickerWidget.label }}
|
|
784
|
+
</label>
|
|
785
|
+
<input
|
|
786
|
+
type="date"
|
|
787
|
+
[id]="inputId"
|
|
788
|
+
[ngClass]="inputClasses"
|
|
789
|
+
[attr.data-action]="datePickerWidget.action"
|
|
790
|
+
[min]="datePickerWidget.minDate || ''"
|
|
791
|
+
[max]="datePickerWidget.maxDate || ''"
|
|
792
|
+
[(ngModel)]="value" />
|
|
793
|
+
</div>
|
|
794
|
+
` }]
|
|
795
|
+
}], propDecorators: { widget: [{
|
|
796
|
+
type: Input
|
|
797
|
+
}] } });
|
|
798
|
+
|
|
799
|
+
class MultiSelectWidgetComponent {
|
|
800
|
+
widget;
|
|
801
|
+
widgetAction;
|
|
802
|
+
values = [];
|
|
803
|
+
selectId = '';
|
|
804
|
+
get multiSelectWidget() {
|
|
805
|
+
return this.widget;
|
|
806
|
+
}
|
|
807
|
+
get showLabel() {
|
|
808
|
+
const widget = this.multiSelectWidget;
|
|
809
|
+
if (widget.hideLabel === true) {
|
|
810
|
+
return false;
|
|
811
|
+
}
|
|
812
|
+
if (widget.showLabel === false) {
|
|
813
|
+
return false;
|
|
814
|
+
}
|
|
815
|
+
return true;
|
|
816
|
+
}
|
|
817
|
+
get selectClasses() {
|
|
818
|
+
return this.isFormAppearance
|
|
819
|
+
? ['bbq-form-multiselect', 'bbq-form-select']
|
|
820
|
+
: ['bbq-form-multiselect', 'bbq-form-select'];
|
|
821
|
+
}
|
|
822
|
+
get isFormAppearance() {
|
|
823
|
+
return this.multiSelectWidget.appearance === 'form';
|
|
824
|
+
}
|
|
825
|
+
ngOnInit() {
|
|
826
|
+
this.selectId = `bbq-${this.multiSelectWidget.action.replace(/\s+/g, '-').toLowerCase()}-select`;
|
|
827
|
+
}
|
|
828
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.8", ngImport: i0, type: MultiSelectWidgetComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
829
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.8", type: MultiSelectWidgetComponent, isStandalone: true, selector: "bbq-multiselect-widget", inputs: { widget: "widget" }, ngImport: i0, template: `
|
|
830
|
+
<div
|
|
831
|
+
class="bbq-widget bbq-multi-select"
|
|
832
|
+
[attr.data-widget-type]="'multiselect'">
|
|
833
|
+
<label *ngIf="showLabel" class="bbq-multi-select-label" [attr.for]="selectId">
|
|
834
|
+
{{ multiSelectWidget.label }}
|
|
835
|
+
</label>
|
|
836
|
+
<select
|
|
837
|
+
[id]="selectId"
|
|
838
|
+
[ngClass]="selectClasses"
|
|
839
|
+
[attr.data-action]="multiSelectWidget.action"
|
|
840
|
+
multiple
|
|
841
|
+
[(ngModel)]="values">
|
|
842
|
+
@for (option of multiSelectWidget.options; track option) {
|
|
843
|
+
<option [value]="option">{{ option }}</option>
|
|
844
|
+
}
|
|
845
|
+
</select>
|
|
846
|
+
</div>
|
|
847
|
+
`, isInline: true, dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i2.NgSelectOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i2.ɵNgSelectMultipleOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i2.SelectMultipleControlValueAccessor, selector: "select[multiple][formControlName],select[multiple][formControl],select[multiple][ngModel]", inputs: ["compareWith"] }, { kind: "directive", type: i2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }] });
|
|
848
|
+
}
|
|
849
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.8", ngImport: i0, type: MultiSelectWidgetComponent, decorators: [{
|
|
850
|
+
type: Component,
|
|
851
|
+
args: [{ selector: 'bbq-multiselect-widget', standalone: true, imports: [CommonModule, FormsModule], template: `
|
|
852
|
+
<div
|
|
853
|
+
class="bbq-widget bbq-multi-select"
|
|
854
|
+
[attr.data-widget-type]="'multiselect'">
|
|
855
|
+
<label *ngIf="showLabel" class="bbq-multi-select-label" [attr.for]="selectId">
|
|
856
|
+
{{ multiSelectWidget.label }}
|
|
857
|
+
</label>
|
|
858
|
+
<select
|
|
859
|
+
[id]="selectId"
|
|
860
|
+
[ngClass]="selectClasses"
|
|
861
|
+
[attr.data-action]="multiSelectWidget.action"
|
|
862
|
+
multiple
|
|
863
|
+
[(ngModel)]="values">
|
|
864
|
+
@for (option of multiSelectWidget.options; track option) {
|
|
865
|
+
<option [value]="option">{{ option }}</option>
|
|
866
|
+
}
|
|
867
|
+
</select>
|
|
868
|
+
</div>
|
|
869
|
+
` }]
|
|
870
|
+
}], propDecorators: { widget: [{
|
|
871
|
+
type: Input
|
|
872
|
+
}] } });
|
|
873
|
+
|
|
874
|
+
class ProgressBarWidgetComponent {
|
|
875
|
+
widget;
|
|
876
|
+
widgetAction;
|
|
877
|
+
progressId = '';
|
|
878
|
+
percentage = 0;
|
|
879
|
+
get progressBarWidget() {
|
|
880
|
+
return this.widget;
|
|
881
|
+
}
|
|
882
|
+
ngOnInit() {
|
|
883
|
+
this.progressId = `bbq-${this.progressBarWidget.action.replace(/\s+/g, '-').toLowerCase()}-progress`;
|
|
884
|
+
const max = this.progressBarWidget.max;
|
|
885
|
+
this.percentage = max > 0 ? Math.floor((this.progressBarWidget.value * 100) / max) : 0;
|
|
886
|
+
}
|
|
887
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.8", ngImport: i0, type: ProgressBarWidgetComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
888
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "21.0.8", type: ProgressBarWidgetComponent, isStandalone: true, selector: "bbq-progressbar-widget", inputs: { widget: "widget" }, ngImport: i0, template: `
|
|
889
|
+
<div
|
|
890
|
+
class="bbq-widget bbq-progress-bar"
|
|
891
|
+
[attr.data-widget-type]="'progressbar'">
|
|
892
|
+
<label class="bbq-progress-bar-label" [attr.for]="progressId">
|
|
893
|
+
{{ progressBarWidget.label }}
|
|
894
|
+
</label>
|
|
895
|
+
<progress
|
|
896
|
+
[id]="progressId"
|
|
897
|
+
class="bbq-progress-bar-element"
|
|
898
|
+
[value]="progressBarWidget.value"
|
|
899
|
+
[max]="progressBarWidget.max"
|
|
900
|
+
[attr.data-action]="progressBarWidget.action"
|
|
901
|
+
[attr.aria-label]="progressBarWidget.label"
|
|
902
|
+
[attr.aria-valuenow]="progressBarWidget.value"
|
|
903
|
+
[attr.aria-valuemin]="0"
|
|
904
|
+
[attr.aria-valuemax]="progressBarWidget.max">
|
|
905
|
+
{{ percentage }}%
|
|
906
|
+
</progress>
|
|
907
|
+
<span class="bbq-progress-bar-value" aria-live="polite">{{ percentage }}%</span>
|
|
908
|
+
</div>
|
|
909
|
+
`, isInline: true, dependencies: [{ kind: "ngmodule", type: CommonModule }] });
|
|
910
|
+
}
|
|
911
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.8", ngImport: i0, type: ProgressBarWidgetComponent, decorators: [{
|
|
912
|
+
type: Component,
|
|
913
|
+
args: [{ selector: 'bbq-progressbar-widget', standalone: true, imports: [CommonModule], template: `
|
|
914
|
+
<div
|
|
915
|
+
class="bbq-widget bbq-progress-bar"
|
|
916
|
+
[attr.data-widget-type]="'progressbar'">
|
|
917
|
+
<label class="bbq-progress-bar-label" [attr.for]="progressId">
|
|
918
|
+
{{ progressBarWidget.label }}
|
|
919
|
+
</label>
|
|
920
|
+
<progress
|
|
921
|
+
[id]="progressId"
|
|
922
|
+
class="bbq-progress-bar-element"
|
|
923
|
+
[value]="progressBarWidget.value"
|
|
924
|
+
[max]="progressBarWidget.max"
|
|
925
|
+
[attr.data-action]="progressBarWidget.action"
|
|
926
|
+
[attr.aria-label]="progressBarWidget.label"
|
|
927
|
+
[attr.aria-valuenow]="progressBarWidget.value"
|
|
928
|
+
[attr.aria-valuemin]="0"
|
|
929
|
+
[attr.aria-valuemax]="progressBarWidget.max">
|
|
930
|
+
{{ percentage }}%
|
|
931
|
+
</progress>
|
|
932
|
+
<span class="bbq-progress-bar-value" aria-live="polite">{{ percentage }}%</span>
|
|
933
|
+
</div>
|
|
934
|
+
` }]
|
|
935
|
+
}], propDecorators: { widget: [{
|
|
936
|
+
type: Input
|
|
937
|
+
}] } });
|
|
938
|
+
|
|
939
|
+
/**
|
|
940
|
+
* Helper class to wrap form fields as widgets for dynamic rendering
|
|
941
|
+
*/
|
|
942
|
+
class FormFieldWidget {
|
|
943
|
+
field;
|
|
944
|
+
formId;
|
|
945
|
+
type;
|
|
946
|
+
label;
|
|
947
|
+
action;
|
|
948
|
+
appearance = 'form';
|
|
949
|
+
hideLabel = true;
|
|
950
|
+
constructor(field, formId) {
|
|
951
|
+
this.field = field;
|
|
952
|
+
this.formId = formId;
|
|
953
|
+
this.type = this.mapFieldTypeToWidgetType(field.type);
|
|
954
|
+
this.label = field.label;
|
|
955
|
+
this.action = `${formId}_${field.name}`;
|
|
956
|
+
}
|
|
957
|
+
mapFieldTypeToWidgetType(fieldType) {
|
|
958
|
+
const typeMap = {
|
|
959
|
+
'input': 'input',
|
|
960
|
+
'text': 'input',
|
|
961
|
+
'email': 'input',
|
|
962
|
+
'number': 'input',
|
|
963
|
+
'password': 'input',
|
|
964
|
+
'textarea': 'textarea',
|
|
965
|
+
'dropdown': 'dropdown',
|
|
966
|
+
'select': 'dropdown',
|
|
967
|
+
'slider': 'slider',
|
|
968
|
+
'toggle': 'toggle',
|
|
969
|
+
'datepicker': 'datepicker',
|
|
970
|
+
'multiselect': 'multiselect',
|
|
971
|
+
'fileupload': 'fileupload'
|
|
972
|
+
};
|
|
973
|
+
return typeMap[fieldType] || 'input';
|
|
974
|
+
}
|
|
975
|
+
// Map field properties to widget properties
|
|
976
|
+
get placeholder() {
|
|
977
|
+
return this.field.placeholder ?? undefined;
|
|
978
|
+
}
|
|
979
|
+
get maxLength() {
|
|
980
|
+
return this.field['maxLength'];
|
|
981
|
+
}
|
|
982
|
+
get rows() {
|
|
983
|
+
return this.field['rows'];
|
|
984
|
+
}
|
|
985
|
+
get options() {
|
|
986
|
+
return this.field['options'] || [];
|
|
987
|
+
}
|
|
988
|
+
get min() {
|
|
989
|
+
return this.field['min'] ?? 0;
|
|
990
|
+
}
|
|
991
|
+
get max() {
|
|
992
|
+
return this.field['max'] ?? 100;
|
|
993
|
+
}
|
|
994
|
+
get step() {
|
|
995
|
+
return this.field['step'] ?? 1;
|
|
996
|
+
}
|
|
997
|
+
get defaultValue() {
|
|
998
|
+
return this.field['defaultValue'] ?? (this.type === 'slider' ? this.min : undefined);
|
|
999
|
+
}
|
|
1000
|
+
get minDate() {
|
|
1001
|
+
return this.field['minDate'];
|
|
1002
|
+
}
|
|
1003
|
+
get maxDate() {
|
|
1004
|
+
return this.field['maxDate'];
|
|
1005
|
+
}
|
|
1006
|
+
get accept() {
|
|
1007
|
+
return this.field['accept'];
|
|
1008
|
+
}
|
|
1009
|
+
get maxBytes() {
|
|
1010
|
+
return this.field['maxBytes'];
|
|
1011
|
+
}
|
|
1012
|
+
// ChatWidget interface methods
|
|
1013
|
+
toJson() {
|
|
1014
|
+
return JSON.stringify(this.toObject());
|
|
1015
|
+
}
|
|
1016
|
+
toObject() {
|
|
1017
|
+
return {
|
|
1018
|
+
type: this.type,
|
|
1019
|
+
label: this.label,
|
|
1020
|
+
action: this.action,
|
|
1021
|
+
...this.field
|
|
1022
|
+
};
|
|
1023
|
+
}
|
|
1024
|
+
}
|
|
1025
|
+
class FormWidgetComponent {
|
|
1026
|
+
injector;
|
|
1027
|
+
environmentInjector;
|
|
1028
|
+
widget;
|
|
1029
|
+
widgetAction;
|
|
1030
|
+
fieldContainers;
|
|
1031
|
+
formId = '';
|
|
1032
|
+
formData = {};
|
|
1033
|
+
showValidationMessage = false;
|
|
1034
|
+
componentRefs = [];
|
|
1035
|
+
// Component registry for field types
|
|
1036
|
+
fieldComponentRegistry = {
|
|
1037
|
+
'input': InputWidgetComponent,
|
|
1038
|
+
'text': InputWidgetComponent,
|
|
1039
|
+
'email': InputWidgetComponent,
|
|
1040
|
+
'number': InputWidgetComponent,
|
|
1041
|
+
'password': InputWidgetComponent,
|
|
1042
|
+
'textarea': TextAreaWidgetComponent,
|
|
1043
|
+
'dropdown': DropdownWidgetComponent,
|
|
1044
|
+
'select': DropdownWidgetComponent,
|
|
1045
|
+
'slider': SliderWidgetComponent,
|
|
1046
|
+
'toggle': ToggleWidgetComponent,
|
|
1047
|
+
'datepicker': DatePickerWidgetComponent,
|
|
1048
|
+
'multiselect': MultiSelectWidgetComponent,
|
|
1049
|
+
'fileupload': FileUploadWidgetComponent,
|
|
1050
|
+
'checkbox': ToggleWidgetComponent,
|
|
1051
|
+
'radio': ToggleWidgetComponent,
|
|
1052
|
+
};
|
|
1053
|
+
get formWidget() {
|
|
1054
|
+
return this.widget;
|
|
1055
|
+
}
|
|
1056
|
+
constructor(injector, environmentInjector) {
|
|
1057
|
+
this.injector = injector;
|
|
1058
|
+
this.environmentInjector = environmentInjector;
|
|
1059
|
+
}
|
|
1060
|
+
ngOnInit() {
|
|
1061
|
+
this.formId = `bbq-${this.formWidget.action.replace(/\s+/g, '-').toLowerCase()}`;
|
|
1062
|
+
// Initialize form data with default values
|
|
1063
|
+
for (const field of this.formWidget.fields || []) {
|
|
1064
|
+
if (field.type === 'slider') {
|
|
1065
|
+
this.formData[field.name] = field['default'] ?? field['defaultValue'] ?? field['min'] ?? 0;
|
|
1066
|
+
}
|
|
1067
|
+
else if (field.type === 'toggle' || field.type === 'checkbox') {
|
|
1068
|
+
this.formData[field.name] = field['defaultValue'] ?? false;
|
|
1069
|
+
}
|
|
1070
|
+
else if (field.type === 'multiselect') {
|
|
1071
|
+
this.formData[field.name] = [];
|
|
1072
|
+
}
|
|
1073
|
+
else {
|
|
1074
|
+
this.formData[field.name] = '';
|
|
1075
|
+
}
|
|
1076
|
+
}
|
|
1077
|
+
}
|
|
1078
|
+
ngAfterViewInit() {
|
|
1079
|
+
// Render field widgets dynamically
|
|
1080
|
+
setTimeout(() => this.renderFieldWidgets(), 0);
|
|
1081
|
+
}
|
|
1082
|
+
ngOnDestroy() {
|
|
1083
|
+
// Clean up component refs
|
|
1084
|
+
this.componentRefs.forEach(ref => ref.destroy());
|
|
1085
|
+
this.componentRefs = [];
|
|
1086
|
+
}
|
|
1087
|
+
renderFieldWidgets() {
|
|
1088
|
+
const containers = this.fieldContainers.toArray();
|
|
1089
|
+
const fields = this.formWidget.fields || [];
|
|
1090
|
+
fields.forEach((field, index) => {
|
|
1091
|
+
const container = containers[index];
|
|
1092
|
+
if (!container)
|
|
1093
|
+
return;
|
|
1094
|
+
const componentType = this.fieldComponentRegistry[field.type];
|
|
1095
|
+
if (!componentType) {
|
|
1096
|
+
// Fallback to input for unknown types
|
|
1097
|
+
this.renderInputFallback(container, field);
|
|
1098
|
+
return;
|
|
1099
|
+
}
|
|
1100
|
+
// Create the field widget
|
|
1101
|
+
const fieldWidget = new FormFieldWidget(field, this.formId);
|
|
1102
|
+
// Create the component
|
|
1103
|
+
const componentRef = createComponent(componentType, {
|
|
1104
|
+
environmentInjector: this.environmentInjector,
|
|
1105
|
+
elementInjector: this.injector,
|
|
1106
|
+
});
|
|
1107
|
+
// Set component inputs
|
|
1108
|
+
const instance = componentRef.instance;
|
|
1109
|
+
instance['widget'] = fieldWidget;
|
|
1110
|
+
// Connect to form data via widgetAction
|
|
1111
|
+
instance['widgetAction'] = (actionName, payload) => {
|
|
1112
|
+
// Handle field value changes - for now, we'll sync via the rendered widget's internal state
|
|
1113
|
+
// The actual form submission will gather values from the DOM
|
|
1114
|
+
};
|
|
1115
|
+
// Attach to container
|
|
1116
|
+
container.insert(componentRef.hostView);
|
|
1117
|
+
this.componentRefs.push(componentRef);
|
|
1118
|
+
// Trigger change detection
|
|
1119
|
+
componentRef.changeDetectorRef.detectChanges();
|
|
1120
|
+
});
|
|
1121
|
+
}
|
|
1122
|
+
renderInputFallback(container, field) {
|
|
1123
|
+
// For unsupported field types, render a basic input
|
|
1124
|
+
const fieldWidget = new FormFieldWidget(field, this.formId);
|
|
1125
|
+
const componentRef = createComponent(InputWidgetComponent, {
|
|
1126
|
+
environmentInjector: this.environmentInjector,
|
|
1127
|
+
elementInjector: this.injector,
|
|
1128
|
+
});
|
|
1129
|
+
const instance = componentRef.instance;
|
|
1130
|
+
instance['widget'] = fieldWidget;
|
|
1131
|
+
container.insert(componentRef.hostView);
|
|
1132
|
+
this.componentRefs.push(componentRef);
|
|
1133
|
+
componentRef.changeDetectorRef.detectChanges();
|
|
1134
|
+
}
|
|
1135
|
+
getFieldId(fieldName) {
|
|
1136
|
+
// Match the ID format used by dynamically rendered input widgets
|
|
1137
|
+
return `bbq-${this.formId}_${fieldName}-input`;
|
|
1138
|
+
}
|
|
1139
|
+
getFieldProp(field, prop) {
|
|
1140
|
+
return field[prop];
|
|
1141
|
+
}
|
|
1142
|
+
onActionClick(actionType) {
|
|
1143
|
+
if (actionType === 'submit') {
|
|
1144
|
+
// Validate required fields
|
|
1145
|
+
const hasErrors = this.validateForm();
|
|
1146
|
+
if (hasErrors) {
|
|
1147
|
+
this.showValidationMessage = true;
|
|
1148
|
+
return;
|
|
1149
|
+
}
|
|
1150
|
+
this.showValidationMessage = false;
|
|
1151
|
+
// Gather form data from the DOM (since widgets manage their own state)
|
|
1152
|
+
this.gatherFormData();
|
|
1153
|
+
if (this.widgetAction) {
|
|
1154
|
+
this.widgetAction(this.formWidget.action, this.formData);
|
|
1155
|
+
}
|
|
1156
|
+
}
|
|
1157
|
+
else {
|
|
1158
|
+
// Cancel or other actions
|
|
1159
|
+
if (this.widgetAction) {
|
|
1160
|
+
this.widgetAction(this.formWidget.action, { actionType });
|
|
1161
|
+
}
|
|
1162
|
+
}
|
|
1163
|
+
}
|
|
1164
|
+
validateForm() {
|
|
1165
|
+
for (const field of this.formWidget.fields || []) {
|
|
1166
|
+
if (field.required) {
|
|
1167
|
+
const value = this.formData[field.name];
|
|
1168
|
+
if (value === undefined || value === null || value === '' ||
|
|
1169
|
+
(Array.isArray(value) && value.length === 0)) {
|
|
1170
|
+
return true; // Has errors
|
|
1171
|
+
}
|
|
1172
|
+
}
|
|
1173
|
+
}
|
|
1174
|
+
return false; // No errors
|
|
1175
|
+
}
|
|
1176
|
+
gatherFormData() {
|
|
1177
|
+
// Gather data from the rendered field widgets
|
|
1178
|
+
// Since each widget component manages its own state via ngModel,
|
|
1179
|
+
// we need to query the DOM to get the current values
|
|
1180
|
+
const fields = this.formWidget.fields || [];
|
|
1181
|
+
fields.forEach((field) => {
|
|
1182
|
+
const fieldId = this.getFieldId(field.name);
|
|
1183
|
+
const element = document.getElementById(fieldId);
|
|
1184
|
+
if (element) {
|
|
1185
|
+
if (element.type === 'checkbox') {
|
|
1186
|
+
this.formData[field.name] = element.checked;
|
|
1187
|
+
}
|
|
1188
|
+
else if (element.type === 'file') {
|
|
1189
|
+
this.formData[field.name] = element.files?.[0];
|
|
1190
|
+
}
|
|
1191
|
+
else if (element.tagName === 'SELECT' && element.multiple) {
|
|
1192
|
+
const select = element;
|
|
1193
|
+
this.formData[field.name] = Array.from(select.selectedOptions).map(opt => opt.value);
|
|
1194
|
+
}
|
|
1195
|
+
else {
|
|
1196
|
+
this.formData[field.name] = element.value;
|
|
1197
|
+
}
|
|
1198
|
+
}
|
|
1199
|
+
});
|
|
1200
|
+
}
|
|
1201
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.8", ngImport: i0, type: FormWidgetComponent, deps: [{ token: i0.Injector }, { token: i0.EnvironmentInjector }], target: i0.ɵɵFactoryTarget.Component });
|
|
1202
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.8", type: FormWidgetComponent, isStandalone: true, selector: "bbq-form-widget", inputs: { widget: "widget" }, viewQueries: [{ propertyName: "fieldContainers", predicate: ["fieldContainer"], descendants: true, read: ViewContainerRef }], ngImport: i0, template: `
|
|
1203
|
+
<div
|
|
1204
|
+
class="bbq-widget bbq-form"
|
|
1205
|
+
[attr.data-widget-id]="formId"
|
|
1206
|
+
[attr.data-widget-type]="'form'"
|
|
1207
|
+
[attr.data-action]="formWidget.action">
|
|
1208
|
+
<fieldset class="bbq-form-fieldset">
|
|
1209
|
+
<legend class="bbq-form-title">{{ formWidget.title }}</legend>
|
|
1210
|
+
|
|
1211
|
+
@for (field of formWidget.fields; track field.name) {
|
|
1212
|
+
<div
|
|
1213
|
+
class="bbq-form-field"
|
|
1214
|
+
[class.bbq-form-field-required]="field.required"
|
|
1215
|
+
[attr.data-required]="field.required ? 'true' : null">
|
|
1216
|
+
<label class="bbq-form-field-label" [attr.for]="getFieldId(field.name)">
|
|
1217
|
+
{{ field.label }}
|
|
1218
|
+
@if (field.required) {
|
|
1219
|
+
<span class="bbq-form-required">*</span>
|
|
1220
|
+
}
|
|
1221
|
+
</label>
|
|
1222
|
+
|
|
1223
|
+
<div #fieldContainer class="bbq-form-field-widget"></div>
|
|
1224
|
+
|
|
1225
|
+
@if (getFieldProp(field, 'validationHint')) {
|
|
1226
|
+
<span class="bbq-form-field-hint">{{ getFieldProp(field, 'validationHint') }}</span>
|
|
1227
|
+
}
|
|
1228
|
+
</div>
|
|
1229
|
+
}
|
|
1230
|
+
|
|
1231
|
+
<div class="bbq-form-validation-message" [style.display]="showValidationMessage ? 'block' : 'none'">
|
|
1232
|
+
Please fill in all required fields before submitting.
|
|
1233
|
+
</div>
|
|
1234
|
+
|
|
1235
|
+
@if (formWidget.actions && formWidget.actions.length > 0) {
|
|
1236
|
+
<div class="bbq-form-actions">
|
|
1237
|
+
@for (action of formWidget.actions; track action.label) {
|
|
1238
|
+
<button
|
|
1239
|
+
type="button"
|
|
1240
|
+
class="bbq-form-button"
|
|
1241
|
+
[class.bbq-form-submit]="action.type === 'submit'"
|
|
1242
|
+
[class.bbq-form-cancel]="action.type !== 'submit'"
|
|
1243
|
+
[attr.data-action]="formWidget.action"
|
|
1244
|
+
[attr.data-action-type]="action.type"
|
|
1245
|
+
(click)="onActionClick(action.type)">
|
|
1246
|
+
{{ action.label }}
|
|
1247
|
+
</button>
|
|
1248
|
+
}
|
|
1249
|
+
</div>
|
|
1250
|
+
}
|
|
1251
|
+
</fieldset>
|
|
1252
|
+
</div>
|
|
1253
|
+
`, isInline: true, styles: [".bbq-form-field-widget{display:contents}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: FormsModule }] });
|
|
1254
|
+
}
|
|
1255
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.8", ngImport: i0, type: FormWidgetComponent, decorators: [{
|
|
1256
|
+
type: Component,
|
|
1257
|
+
args: [{ selector: 'bbq-form-widget', standalone: true, imports: [CommonModule, FormsModule], template: `
|
|
1258
|
+
<div
|
|
1259
|
+
class="bbq-widget bbq-form"
|
|
1260
|
+
[attr.data-widget-id]="formId"
|
|
1261
|
+
[attr.data-widget-type]="'form'"
|
|
1262
|
+
[attr.data-action]="formWidget.action">
|
|
1263
|
+
<fieldset class="bbq-form-fieldset">
|
|
1264
|
+
<legend class="bbq-form-title">{{ formWidget.title }}</legend>
|
|
1265
|
+
|
|
1266
|
+
@for (field of formWidget.fields; track field.name) {
|
|
1267
|
+
<div
|
|
1268
|
+
class="bbq-form-field"
|
|
1269
|
+
[class.bbq-form-field-required]="field.required"
|
|
1270
|
+
[attr.data-required]="field.required ? 'true' : null">
|
|
1271
|
+
<label class="bbq-form-field-label" [attr.for]="getFieldId(field.name)">
|
|
1272
|
+
{{ field.label }}
|
|
1273
|
+
@if (field.required) {
|
|
1274
|
+
<span class="bbq-form-required">*</span>
|
|
1275
|
+
}
|
|
1276
|
+
</label>
|
|
1277
|
+
|
|
1278
|
+
<div #fieldContainer class="bbq-form-field-widget"></div>
|
|
1279
|
+
|
|
1280
|
+
@if (getFieldProp(field, 'validationHint')) {
|
|
1281
|
+
<span class="bbq-form-field-hint">{{ getFieldProp(field, 'validationHint') }}</span>
|
|
1282
|
+
}
|
|
1283
|
+
</div>
|
|
1284
|
+
}
|
|
1285
|
+
|
|
1286
|
+
<div class="bbq-form-validation-message" [style.display]="showValidationMessage ? 'block' : 'none'">
|
|
1287
|
+
Please fill in all required fields before submitting.
|
|
1288
|
+
</div>
|
|
1289
|
+
|
|
1290
|
+
@if (formWidget.actions && formWidget.actions.length > 0) {
|
|
1291
|
+
<div class="bbq-form-actions">
|
|
1292
|
+
@for (action of formWidget.actions; track action.label) {
|
|
1293
|
+
<button
|
|
1294
|
+
type="button"
|
|
1295
|
+
class="bbq-form-button"
|
|
1296
|
+
[class.bbq-form-submit]="action.type === 'submit'"
|
|
1297
|
+
[class.bbq-form-cancel]="action.type !== 'submit'"
|
|
1298
|
+
[attr.data-action]="formWidget.action"
|
|
1299
|
+
[attr.data-action-type]="action.type"
|
|
1300
|
+
(click)="onActionClick(action.type)">
|
|
1301
|
+
{{ action.label }}
|
|
1302
|
+
</button>
|
|
1303
|
+
}
|
|
1304
|
+
</div>
|
|
1305
|
+
}
|
|
1306
|
+
</fieldset>
|
|
1307
|
+
</div>
|
|
1308
|
+
`, styles: [".bbq-form-field-widget{display:contents}\n"] }]
|
|
1309
|
+
}], ctorParameters: () => [{ type: i0.Injector }, { type: i0.EnvironmentInjector }], propDecorators: { widget: [{
|
|
1310
|
+
type: Input
|
|
1311
|
+
}], fieldContainers: [{
|
|
1312
|
+
type: ViewChildren,
|
|
1313
|
+
args: ['fieldContainer', { read: ViewContainerRef }]
|
|
1314
|
+
}] } });
|
|
1315
|
+
|
|
1316
|
+
class ImageWidgetComponent {
|
|
1317
|
+
widget;
|
|
1318
|
+
widgetAction;
|
|
1319
|
+
get imageWidget() {
|
|
1320
|
+
return this.widget;
|
|
1321
|
+
}
|
|
1322
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.8", ngImport: i0, type: ImageWidgetComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
1323
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "21.0.8", type: ImageWidgetComponent, isStandalone: true, selector: "bbq-image-widget", inputs: { widget: "widget" }, ngImport: i0, template: `
|
|
1324
|
+
<div
|
|
1325
|
+
class="bbq-widget bbq-image"
|
|
1326
|
+
[attr.data-widget-type]="'image'"
|
|
1327
|
+
[attr.data-action]="imageWidget.action">
|
|
1328
|
+
<img
|
|
1329
|
+
class="bbq-image-img"
|
|
1330
|
+
[src]="imageWidget.imageUrl"
|
|
1331
|
+
[alt]="imageWidget.alt || 'Image'"
|
|
1332
|
+
[style.width]="imageWidget.width ? imageWidget.width + 'px' : 'auto'"
|
|
1333
|
+
[style.height]="imageWidget.height ? imageWidget.height + 'px' : 'auto'"
|
|
1334
|
+
loading="lazy" />
|
|
1335
|
+
</div>
|
|
1336
|
+
`, isInline: true, dependencies: [{ kind: "ngmodule", type: CommonModule }] });
|
|
1337
|
+
}
|
|
1338
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.8", ngImport: i0, type: ImageWidgetComponent, decorators: [{
|
|
1339
|
+
type: Component,
|
|
1340
|
+
args: [{ selector: 'bbq-image-widget', standalone: true, imports: [CommonModule], template: `
|
|
1341
|
+
<div
|
|
1342
|
+
class="bbq-widget bbq-image"
|
|
1343
|
+
[attr.data-widget-type]="'image'"
|
|
1344
|
+
[attr.data-action]="imageWidget.action">
|
|
1345
|
+
<img
|
|
1346
|
+
class="bbq-image-img"
|
|
1347
|
+
[src]="imageWidget.imageUrl"
|
|
1348
|
+
[alt]="imageWidget.alt || 'Image'"
|
|
1349
|
+
[style.width]="imageWidget.width ? imageWidget.width + 'px' : 'auto'"
|
|
1350
|
+
[style.height]="imageWidget.height ? imageWidget.height + 'px' : 'auto'"
|
|
1351
|
+
loading="lazy" />
|
|
1352
|
+
</div>
|
|
1353
|
+
` }]
|
|
1354
|
+
}], propDecorators: { widget: [{
|
|
1355
|
+
type: Input
|
|
1356
|
+
}] } });
|
|
1357
|
+
|
|
1358
|
+
class ImageCollectionWidgetComponent {
|
|
1359
|
+
widget;
|
|
1360
|
+
widgetAction;
|
|
1361
|
+
get imageCollectionWidget() {
|
|
1362
|
+
return this.widget;
|
|
1363
|
+
}
|
|
1364
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.8", ngImport: i0, type: ImageCollectionWidgetComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
1365
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.8", type: ImageCollectionWidgetComponent, isStandalone: true, selector: "bbq-imagecollection-widget", inputs: { widget: "widget" }, ngImport: i0, template: `
|
|
1366
|
+
<div
|
|
1367
|
+
class="bbq-widget bbq-image-collection"
|
|
1368
|
+
[attr.data-widget-type]="'imagecollection'"
|
|
1369
|
+
[attr.data-action]="imageCollectionWidget.action">
|
|
1370
|
+
<div class="bbq-image-collection-grid">
|
|
1371
|
+
@for (image of imageCollectionWidget.images; track image.imageUrl) {
|
|
1372
|
+
<div class="bbq-image-collection-item">
|
|
1373
|
+
<img
|
|
1374
|
+
class="bbq-image-collection-img"
|
|
1375
|
+
[src]="image.imageUrl"
|
|
1376
|
+
[alt]="image.alt || 'Image'"
|
|
1377
|
+
[style.width]="image.width ? image.width + 'px' : 'auto'"
|
|
1378
|
+
[style.height]="image.height ? image.height + 'px' : 'auto'"
|
|
1379
|
+
loading="lazy" />
|
|
1380
|
+
</div>
|
|
1381
|
+
}
|
|
1382
|
+
</div>
|
|
1383
|
+
</div>
|
|
1384
|
+
`, isInline: true, dependencies: [{ kind: "ngmodule", type: CommonModule }] });
|
|
1385
|
+
}
|
|
1386
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.8", ngImport: i0, type: ImageCollectionWidgetComponent, decorators: [{
|
|
1387
|
+
type: Component,
|
|
1388
|
+
args: [{ selector: 'bbq-imagecollection-widget', standalone: true, imports: [CommonModule], template: `
|
|
1389
|
+
<div
|
|
1390
|
+
class="bbq-widget bbq-image-collection"
|
|
1391
|
+
[attr.data-widget-type]="'imagecollection'"
|
|
1392
|
+
[attr.data-action]="imageCollectionWidget.action">
|
|
1393
|
+
<div class="bbq-image-collection-grid">
|
|
1394
|
+
@for (image of imageCollectionWidget.images; track image.imageUrl) {
|
|
1395
|
+
<div class="bbq-image-collection-item">
|
|
1396
|
+
<img
|
|
1397
|
+
class="bbq-image-collection-img"
|
|
1398
|
+
[src]="image.imageUrl"
|
|
1399
|
+
[alt]="image.alt || 'Image'"
|
|
1400
|
+
[style.width]="image.width ? image.width + 'px' : 'auto'"
|
|
1401
|
+
[style.height]="image.height ? image.height + 'px' : 'auto'"
|
|
1402
|
+
loading="lazy" />
|
|
1403
|
+
</div>
|
|
1404
|
+
}
|
|
1405
|
+
</div>
|
|
1406
|
+
</div>
|
|
1407
|
+
` }]
|
|
1408
|
+
}], propDecorators: { widget: [{
|
|
1409
|
+
type: Input
|
|
1410
|
+
}] } });
|
|
1411
|
+
|
|
1412
|
+
/**
|
|
1413
|
+
* Built-in widget components
|
|
1414
|
+
* These components provide Angular implementations for all standard widget types
|
|
1415
|
+
*/
|
|
1416
|
+
|
|
1417
|
+
/**
|
|
1418
|
+
* Registry of all built-in widget components
|
|
1419
|
+
* Maps widget type to Angular component class
|
|
1420
|
+
*/
|
|
1421
|
+
const BUILT_IN_WIDGET_COMPONENTS = {
|
|
1422
|
+
button: ButtonWidgetComponent,
|
|
1423
|
+
card: CardWidgetComponent,
|
|
1424
|
+
input: InputWidgetComponent,
|
|
1425
|
+
textarea: TextAreaWidgetComponent,
|
|
1426
|
+
dropdown: DropdownWidgetComponent,
|
|
1427
|
+
slider: SliderWidgetComponent,
|
|
1428
|
+
toggle: ToggleWidgetComponent,
|
|
1429
|
+
fileupload: FileUploadWidgetComponent,
|
|
1430
|
+
themeswitcher: ThemeSwitcherWidgetComponent,
|
|
1431
|
+
datepicker: DatePickerWidgetComponent,
|
|
1432
|
+
multiselect: MultiSelectWidgetComponent,
|
|
1433
|
+
progressbar: ProgressBarWidgetComponent,
|
|
1434
|
+
form: FormWidgetComponent,
|
|
1435
|
+
image: ImageWidgetComponent,
|
|
1436
|
+
imagecollection: ImageCollectionWidgetComponent,
|
|
1437
|
+
};
|
|
1438
|
+
|
|
1439
|
+
const WIDGET_EVENT_MANAGER_FACTORY = new InjectionToken('WIDGET_EVENT_MANAGER_FACTORY');
|
|
1440
|
+
/**
|
|
1441
|
+
* Injection token for SsrWidgetRenderer
|
|
1442
|
+
*
|
|
1443
|
+
* Use this token to inject a SsrWidgetRenderer instance in your components.
|
|
1444
|
+
* By default, WidgetRendererComponent provides this token with a factory that creates
|
|
1445
|
+
* a new instance for each component.
|
|
1446
|
+
*
|
|
1447
|
+
* @example
|
|
1448
|
+
* ```typescript
|
|
1449
|
+
* constructor(@Inject(SSR_WIDGET_RENDERER) private renderer: SsrWidgetRenderer) {}
|
|
1450
|
+
* ```
|
|
1451
|
+
*/
|
|
1452
|
+
const SSR_WIDGET_RENDERER = new InjectionToken('SSR_WIDGET_RENDERER');
|
|
1453
|
+
/**
|
|
1454
|
+
* Injection token for AngularWidgetRenderer
|
|
1455
|
+
*
|
|
1456
|
+
* Use this token to inject an AngularWidgetRenderer instance in your components.
|
|
1457
|
+
* This is the recommended renderer for Angular applications as it provides
|
|
1458
|
+
* native Angular component rendering instead of HTML string rendering.
|
|
1459
|
+
*
|
|
1460
|
+
* @example
|
|
1461
|
+
* ```typescript
|
|
1462
|
+
* constructor(@Inject(ANGULAR_WIDGET_RENDERER) private renderer: AngularWidgetRenderer) {}
|
|
1463
|
+
* ```
|
|
1464
|
+
*/
|
|
1465
|
+
const ANGULAR_WIDGET_RENDERER = new InjectionToken('ANGULAR_WIDGET_RENDERER');
|
|
1466
|
+
/**
|
|
1467
|
+
* Factory function for creating WidgetEventManager instances
|
|
1468
|
+
*
|
|
1469
|
+
* This factory is used by default in WidgetRendererComponent's providers array.
|
|
1470
|
+
* You can override this in your own providers if you need custom initialization.
|
|
1471
|
+
*
|
|
1472
|
+
* @returns A factory function that creates WidgetEventManager instances
|
|
1473
|
+
*/
|
|
1474
|
+
function widgetEventManagerFactoryProvider() {
|
|
1475
|
+
return (actionHandler) => new WidgetEventManager(actionHandler);
|
|
1476
|
+
}
|
|
1477
|
+
/**
|
|
1478
|
+
* Factory function for creating SsrWidgetRenderer instances
|
|
1479
|
+
*
|
|
1480
|
+
* This factory is used by default in WidgetRendererComponent's providers array.
|
|
1481
|
+
* You can override this in your own providers if you need custom initialization
|
|
1482
|
+
* or custom rendering options.
|
|
1483
|
+
*
|
|
1484
|
+
* @returns A new SsrWidgetRenderer instance
|
|
1485
|
+
*/
|
|
1486
|
+
function ssrWidgetRendererFactory() {
|
|
1487
|
+
return new SsrWidgetRenderer();
|
|
1488
|
+
}
|
|
1489
|
+
/**
|
|
1490
|
+
* Factory function for creating AngularWidgetRenderer instances
|
|
1491
|
+
*
|
|
1492
|
+
* This factory creates an AngularWidgetRenderer with all built-in widget components
|
|
1493
|
+
* pre-registered. This is the recommended renderer for Angular applications.
|
|
1494
|
+
*
|
|
1495
|
+
* @returns A new AngularWidgetRenderer instance with built-in components registered
|
|
1496
|
+
*/
|
|
1497
|
+
function angularWidgetRendererFactory() {
|
|
1498
|
+
const renderer = new AngularWidgetRenderer();
|
|
1499
|
+
renderer.registerBuiltInComponents(BUILT_IN_WIDGET_COMPONENTS);
|
|
1500
|
+
return renderer;
|
|
1501
|
+
}
|
|
1502
|
+
|
|
1503
|
+
/**
|
|
1504
|
+
* Service for registering custom widget factories and renderers
|
|
1505
|
+
*
|
|
1506
|
+
* This service provides a centralized way to register custom widget types
|
|
1507
|
+
* that extend the base widget functionality, including support for
|
|
1508
|
+
* Angular components and templates as custom renderers.
|
|
1509
|
+
*
|
|
1510
|
+
* @example
|
|
1511
|
+
* ```typescript
|
|
1512
|
+
* constructor(private widgetRegistry: WidgetRegistryService) {
|
|
1513
|
+
* // Register a widget factory
|
|
1514
|
+
* this.widgetRegistry.registerFactory('myWidget', (obj) => {
|
|
1515
|
+
* if (obj.type === 'myWidget') {
|
|
1516
|
+
* return new MyCustomWidget(obj.label, obj.action);
|
|
1517
|
+
* }
|
|
1518
|
+
* return null;
|
|
1519
|
+
* });
|
|
1520
|
+
*
|
|
1521
|
+
* // Register a component-based renderer
|
|
1522
|
+
* this.widgetRegistry.registerRenderer('myWidget', MyWidgetComponent);
|
|
1523
|
+
* }
|
|
1524
|
+
* ```
|
|
1525
|
+
*/
|
|
1526
|
+
class WidgetRegistryService {
|
|
1527
|
+
customRenderers = new Map();
|
|
1528
|
+
/**
|
|
1529
|
+
* Register a custom widget factory function
|
|
1530
|
+
*
|
|
1531
|
+
* @param type - The widget type identifier
|
|
1532
|
+
* @param factory - Factory function that creates widget instances from plain objects
|
|
1533
|
+
*/
|
|
1534
|
+
registerFactory(type, factory) {
|
|
1535
|
+
customWidgetRegistry.registerFactory(type, factory);
|
|
1536
|
+
}
|
|
1537
|
+
/**
|
|
1538
|
+
* Register a widget class with automatic factory creation
|
|
1539
|
+
*
|
|
1540
|
+
* @param type - The widget type identifier
|
|
1541
|
+
* @param ctor - Widget class constructor
|
|
1542
|
+
*/
|
|
1543
|
+
registerClass(type, ctor) {
|
|
1544
|
+
customWidgetRegistry.registerClass(type, ctor);
|
|
1545
|
+
}
|
|
1546
|
+
/**
|
|
1547
|
+
* Get a factory for a specific widget type
|
|
1548
|
+
*
|
|
1549
|
+
* @param type - The widget type identifier
|
|
1550
|
+
* @returns The factory function if registered, undefined otherwise
|
|
1551
|
+
*/
|
|
1552
|
+
getFactory(type) {
|
|
1553
|
+
return customWidgetRegistry.getFactory(type);
|
|
1554
|
+
}
|
|
1555
|
+
/**
|
|
1556
|
+
* Register a custom renderer for a specific widget type
|
|
1557
|
+
*
|
|
1558
|
+
* The renderer can be:
|
|
1559
|
+
* - A function that returns HTML string
|
|
1560
|
+
* - An Angular Component class
|
|
1561
|
+
* - An Angular TemplateRef
|
|
1562
|
+
*
|
|
1563
|
+
* @param type - The widget type identifier
|
|
1564
|
+
* @param renderer - The custom renderer (function, Component, or TemplateRef)
|
|
1565
|
+
*
|
|
1566
|
+
* @example
|
|
1567
|
+
* ```typescript
|
|
1568
|
+
* // HTML function renderer
|
|
1569
|
+
* widgetRegistry.registerRenderer('weather', (widget) => `<div>${widget.label}</div>`);
|
|
1570
|
+
*
|
|
1571
|
+
* // Component renderer
|
|
1572
|
+
* widgetRegistry.registerRenderer('weather', WeatherWidgetComponent);
|
|
1573
|
+
*
|
|
1574
|
+
* // Template renderer (from @ViewChild or elsewhere)
|
|
1575
|
+
* widgetRegistry.registerRenderer('weather', this.weatherTemplate);
|
|
1576
|
+
* ```
|
|
1577
|
+
*/
|
|
1578
|
+
registerRenderer(type, renderer) {
|
|
1579
|
+
if (!type || typeof type !== 'string') {
|
|
1580
|
+
throw new Error('type must be a non-empty string');
|
|
1581
|
+
}
|
|
1582
|
+
if (!renderer) {
|
|
1583
|
+
throw new Error('renderer is required');
|
|
1584
|
+
}
|
|
1585
|
+
this.customRenderers.set(type, renderer);
|
|
1586
|
+
}
|
|
1587
|
+
/**
|
|
1588
|
+
* Get a custom renderer for a specific widget type
|
|
1589
|
+
*
|
|
1590
|
+
* @param type - The widget type identifier
|
|
1591
|
+
* @returns The custom renderer if registered, undefined otherwise
|
|
1592
|
+
*/
|
|
1593
|
+
getRenderer(type) {
|
|
1594
|
+
return this.customRenderers.get(type);
|
|
1595
|
+
}
|
|
1596
|
+
/**
|
|
1597
|
+
* Check if a custom renderer is registered for a widget type
|
|
1598
|
+
*
|
|
1599
|
+
* @param type - The widget type identifier
|
|
1600
|
+
* @returns True if a custom renderer is registered, false otherwise
|
|
1601
|
+
*/
|
|
1602
|
+
hasRenderer(type) {
|
|
1603
|
+
return this.customRenderers.has(type);
|
|
1604
|
+
}
|
|
1605
|
+
/**
|
|
1606
|
+
* Unregister a custom renderer for a widget type
|
|
1607
|
+
*
|
|
1608
|
+
* @param type - The widget type identifier
|
|
1609
|
+
* @returns True if a renderer was removed, false if none was registered
|
|
1610
|
+
*/
|
|
1611
|
+
unregisterRenderer(type) {
|
|
1612
|
+
return this.customRenderers.delete(type);
|
|
1613
|
+
}
|
|
1614
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.8", ngImport: i0, type: WidgetRegistryService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
1615
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.0.8", ngImport: i0, type: WidgetRegistryService, providedIn: 'root' });
|
|
1616
|
+
}
|
|
1617
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.8", ngImport: i0, type: WidgetRegistryService, decorators: [{
|
|
1618
|
+
type: Injectable,
|
|
1619
|
+
args: [{
|
|
1620
|
+
providedIn: 'root',
|
|
1621
|
+
}]
|
|
1622
|
+
}] });
|
|
1623
|
+
|
|
1624
|
+
/**
|
|
1625
|
+
* Angular component for rendering chat widgets
|
|
1626
|
+
*
|
|
1627
|
+
* This component handles rendering of chat widgets using the BbQ ChatWidgets library.
|
|
1628
|
+
* It manages widget lifecycle, event handling, and cleanup.
|
|
1629
|
+
*
|
|
1630
|
+
* Supports three types of custom widget renderers:
|
|
1631
|
+
* 1. HTML function renderers (return HTML strings)
|
|
1632
|
+
* 2. Angular Component renderers (render as dynamic components)
|
|
1633
|
+
* 3. Angular TemplateRef renderers (render as embedded views)
|
|
1634
|
+
*
|
|
1635
|
+
* @example
|
|
1636
|
+
* ```typescript
|
|
1637
|
+
* <bbq-widget-renderer
|
|
1638
|
+
* [widgets]="messageWidgets"
|
|
1639
|
+
* (widgetAction)="handleWidgetAction($event)">
|
|
1640
|
+
* </bbq-widget-renderer>
|
|
1641
|
+
* ```
|
|
1642
|
+
*/
|
|
1643
|
+
class WidgetRendererComponent {
|
|
1644
|
+
renderer;
|
|
1645
|
+
angularRenderer;
|
|
1646
|
+
eventManagerFactory;
|
|
1647
|
+
widgetRegistry;
|
|
1648
|
+
injector;
|
|
1649
|
+
environmentInjector;
|
|
1650
|
+
/**
|
|
1651
|
+
* Array of widgets to render
|
|
1652
|
+
*/
|
|
1653
|
+
widgets;
|
|
1654
|
+
/**
|
|
1655
|
+
* Emits when a widget action is triggered
|
|
1656
|
+
*/
|
|
1657
|
+
widgetAction = new EventEmitter();
|
|
1658
|
+
containerRef;
|
|
1659
|
+
widgetItems = [];
|
|
1660
|
+
eventManager;
|
|
1661
|
+
isViewInitialized = false;
|
|
1662
|
+
dynamicComponents = [];
|
|
1663
|
+
dynamicViews = [];
|
|
1664
|
+
constructor(renderer, angularRenderer, eventManagerFactory, widgetRegistry, injector, environmentInjector) {
|
|
1665
|
+
this.renderer = renderer;
|
|
1666
|
+
this.angularRenderer = angularRenderer;
|
|
1667
|
+
this.eventManagerFactory = eventManagerFactory;
|
|
1668
|
+
this.widgetRegistry = widgetRegistry;
|
|
1669
|
+
this.injector = injector;
|
|
1670
|
+
this.environmentInjector = environmentInjector;
|
|
1671
|
+
}
|
|
1672
|
+
ngOnInit() {
|
|
1673
|
+
// this.updateWidgetHtml();
|
|
1674
|
+
}
|
|
1675
|
+
ngOnChanges(changes) {
|
|
1676
|
+
if (changes['widgets']) {
|
|
1677
|
+
this.updateWidgetHtml();
|
|
1678
|
+
}
|
|
1679
|
+
}
|
|
1680
|
+
ngAfterViewInit() {
|
|
1681
|
+
this.updateWidgetHtml();
|
|
1682
|
+
this.isViewInitialized = true;
|
|
1683
|
+
this.setupEventHandlers();
|
|
1684
|
+
// Render dynamic components/templates after view init
|
|
1685
|
+
this.renderDynamicWidgets();
|
|
1686
|
+
}
|
|
1687
|
+
ngOnDestroy() {
|
|
1688
|
+
this.cleanup();
|
|
1689
|
+
}
|
|
1690
|
+
/**
|
|
1691
|
+
* Base implementation for updating the rendered HTML for the current widgets.
|
|
1692
|
+
*
|
|
1693
|
+
* Subclasses may override this method to customize how widgets are rendered
|
|
1694
|
+
* (for example, to inject additional markup or perform preprocessing).
|
|
1695
|
+
*
|
|
1696
|
+
* Since this is the base implementation, overriding implementations are not
|
|
1697
|
+
* required to call `super.updateWidgetHtml()`.
|
|
1698
|
+
*/
|
|
1699
|
+
updateWidgetHtml() {
|
|
1700
|
+
if (!this.widgets || this.widgets.length === 0) {
|
|
1701
|
+
this.widgetItems = [];
|
|
1702
|
+
return;
|
|
1703
|
+
}
|
|
1704
|
+
this.widgetItems = this.widgets.map((widget, index) => {
|
|
1705
|
+
const customRenderer = this.widgetRegistry.getRenderer(widget.type);
|
|
1706
|
+
// Check template renderer first (most specific)
|
|
1707
|
+
if (customRenderer && isTemplateRenderer(customRenderer)) {
|
|
1708
|
+
return {
|
|
1709
|
+
index,
|
|
1710
|
+
widget,
|
|
1711
|
+
isHtml: false,
|
|
1712
|
+
};
|
|
1713
|
+
}
|
|
1714
|
+
// Check component renderer second
|
|
1715
|
+
if (customRenderer && isComponentRenderer(customRenderer)) {
|
|
1716
|
+
return {
|
|
1717
|
+
index,
|
|
1718
|
+
widget,
|
|
1719
|
+
isHtml: false,
|
|
1720
|
+
};
|
|
1721
|
+
}
|
|
1722
|
+
// Check HTML function renderer last (most general, matches any function)
|
|
1723
|
+
if (customRenderer && isHtmlRenderer(customRenderer)) {
|
|
1724
|
+
return {
|
|
1725
|
+
index,
|
|
1726
|
+
widget,
|
|
1727
|
+
isHtml: true,
|
|
1728
|
+
html: customRenderer(widget),
|
|
1729
|
+
};
|
|
1730
|
+
}
|
|
1731
|
+
// Try to use AngularWidgetRenderer for built-in widgets
|
|
1732
|
+
if (this.angularRenderer) {
|
|
1733
|
+
const componentType = this.angularRenderer.getComponentType(widget);
|
|
1734
|
+
if (componentType) {
|
|
1735
|
+
return {
|
|
1736
|
+
index,
|
|
1737
|
+
widget,
|
|
1738
|
+
isHtml: false,
|
|
1739
|
+
};
|
|
1740
|
+
}
|
|
1741
|
+
}
|
|
1742
|
+
// Fallback: render using the SSR library renderer
|
|
1743
|
+
return {
|
|
1744
|
+
index,
|
|
1745
|
+
widget,
|
|
1746
|
+
isHtml: true,
|
|
1747
|
+
html: this.renderer.renderWidget(widget),
|
|
1748
|
+
};
|
|
1749
|
+
});
|
|
1750
|
+
// After view updates, reinitialize widgets only if view is already initialized
|
|
1751
|
+
if (this.isViewInitialized) {
|
|
1752
|
+
setTimeout(() => {
|
|
1753
|
+
this.setupEventHandlers();
|
|
1754
|
+
this.renderDynamicWidgets();
|
|
1755
|
+
}, 0);
|
|
1756
|
+
}
|
|
1757
|
+
}
|
|
1758
|
+
/**
|
|
1759
|
+
* Render dynamic components and templates for custom widgets
|
|
1760
|
+
*/
|
|
1761
|
+
renderDynamicWidgets() {
|
|
1762
|
+
if (!this.containerRef?.nativeElement)
|
|
1763
|
+
return;
|
|
1764
|
+
// Use microtask to ensure Angular has completed change detection
|
|
1765
|
+
Promise.resolve().then(() => {
|
|
1766
|
+
if (!this.containerRef?.nativeElement)
|
|
1767
|
+
return;
|
|
1768
|
+
// Clean up existing dynamic components and views
|
|
1769
|
+
this.cleanupDynamicWidgets();
|
|
1770
|
+
const container = this.containerRef.nativeElement;
|
|
1771
|
+
// Query all widget divs without the data-rendered filter
|
|
1772
|
+
const dynamicWidgetDivs = Array.from(container.querySelectorAll('.bbq-widget'));
|
|
1773
|
+
let dynamicIndex = 0;
|
|
1774
|
+
this.widgetItems.forEach((item) => {
|
|
1775
|
+
if (!item.isHtml) {
|
|
1776
|
+
const customRenderer = this.widgetRegistry.getRenderer(item.widget.type);
|
|
1777
|
+
const targetDiv = dynamicWidgetDivs[dynamicIndex];
|
|
1778
|
+
if (!targetDiv)
|
|
1779
|
+
return;
|
|
1780
|
+
// Clear the div content before rendering
|
|
1781
|
+
targetDiv.innerHTML = '';
|
|
1782
|
+
// Handle custom renderers first
|
|
1783
|
+
if (customRenderer) {
|
|
1784
|
+
if (isComponentRenderer(customRenderer)) {
|
|
1785
|
+
this.renderComponent(customRenderer, item.widget, targetDiv);
|
|
1786
|
+
}
|
|
1787
|
+
else if (isTemplateRenderer(customRenderer)) {
|
|
1788
|
+
this.renderTemplate(customRenderer, item.widget, targetDiv);
|
|
1789
|
+
}
|
|
1790
|
+
}
|
|
1791
|
+
else if (this.angularRenderer) {
|
|
1792
|
+
// Try to render using AngularWidgetRenderer for built-in widgets
|
|
1793
|
+
const componentType = this.angularRenderer.getComponentType(item.widget);
|
|
1794
|
+
if (componentType) {
|
|
1795
|
+
this.renderComponent(componentType, item.widget, targetDiv);
|
|
1796
|
+
}
|
|
1797
|
+
}
|
|
1798
|
+
dynamicIndex++;
|
|
1799
|
+
}
|
|
1800
|
+
});
|
|
1801
|
+
});
|
|
1802
|
+
}
|
|
1803
|
+
/**
|
|
1804
|
+
* Render an Angular component for a custom widget
|
|
1805
|
+
*
|
|
1806
|
+
* Note: This method safely assigns properties to component instances
|
|
1807
|
+
* by checking for property existence at runtime. This approach is necessary
|
|
1808
|
+
* because we cannot statically verify that all components implement
|
|
1809
|
+
* the CustomWidgetComponent interface.
|
|
1810
|
+
*/
|
|
1811
|
+
renderComponent(componentType, widget, targetElement) {
|
|
1812
|
+
// Create the component using Angular's createComponent API
|
|
1813
|
+
const componentRef = createComponent(componentType, {
|
|
1814
|
+
environmentInjector: this.environmentInjector,
|
|
1815
|
+
elementInjector: this.injector,
|
|
1816
|
+
});
|
|
1817
|
+
// Safely set component inputs if they exist
|
|
1818
|
+
const instance = componentRef.instance;
|
|
1819
|
+
// Set widget property if it exists in the prototype chain
|
|
1820
|
+
if (!instance['widget']) {
|
|
1821
|
+
instance['widget'] = widget;
|
|
1822
|
+
}
|
|
1823
|
+
// Set widgetAction property if it exists in the prototype chain
|
|
1824
|
+
if (!instance['widgetAction']) {
|
|
1825
|
+
instance['widgetAction'] = (actionName, payload) => {
|
|
1826
|
+
this.widgetAction.emit({ actionName, payload });
|
|
1827
|
+
};
|
|
1828
|
+
}
|
|
1829
|
+
// Attach the component's host view to the target element
|
|
1830
|
+
targetElement.appendChild(componentRef.location.nativeElement);
|
|
1831
|
+
// Store reference for cleanup
|
|
1832
|
+
this.dynamicComponents.push(componentRef);
|
|
1833
|
+
// Trigger change detection (use optional chaining for safety)
|
|
1834
|
+
componentRef.changeDetectorRef?.detectChanges();
|
|
1835
|
+
}
|
|
1836
|
+
/**
|
|
1837
|
+
* Render an Angular template for a custom widget
|
|
1838
|
+
*/
|
|
1839
|
+
renderTemplate(templateRef, widget, targetElement) {
|
|
1840
|
+
const context = {
|
|
1841
|
+
$implicit: widget,
|
|
1842
|
+
widget: widget,
|
|
1843
|
+
emitAction: (actionName, payload) => {
|
|
1844
|
+
this.widgetAction.emit({ actionName, payload });
|
|
1845
|
+
},
|
|
1846
|
+
};
|
|
1847
|
+
const viewRef = templateRef.createEmbeddedView(context);
|
|
1848
|
+
// Attach the view's DOM nodes to the target element
|
|
1849
|
+
viewRef.rootNodes.forEach((node) => {
|
|
1850
|
+
targetElement.appendChild(node);
|
|
1851
|
+
});
|
|
1852
|
+
// Store reference for cleanup
|
|
1853
|
+
this.dynamicViews.push(viewRef);
|
|
1854
|
+
// Trigger change detection
|
|
1855
|
+
viewRef.detectChanges();
|
|
1856
|
+
}
|
|
1857
|
+
/**
|
|
1858
|
+
* Cleanup dynamic components and views
|
|
1859
|
+
*/
|
|
1860
|
+
cleanupDynamicWidgets() {
|
|
1861
|
+
this.dynamicComponents.forEach((componentRef) => {
|
|
1862
|
+
componentRef.destroy();
|
|
1863
|
+
});
|
|
1864
|
+
this.dynamicComponents = [];
|
|
1865
|
+
this.dynamicViews.forEach((viewRef) => {
|
|
1866
|
+
viewRef.destroy();
|
|
1867
|
+
});
|
|
1868
|
+
this.dynamicViews = [];
|
|
1869
|
+
}
|
|
1870
|
+
setupEventHandlers() {
|
|
1871
|
+
if (!this.containerRef?.nativeElement)
|
|
1872
|
+
return;
|
|
1873
|
+
// Cleanup old resources before setting up new ones
|
|
1874
|
+
this.cleanup();
|
|
1875
|
+
const container = this.containerRef.nativeElement;
|
|
1876
|
+
// Create a custom action handler that emits events
|
|
1877
|
+
const actionHandler = {
|
|
1878
|
+
handle: async (action, payload) => {
|
|
1879
|
+
this.widgetAction.emit({ actionName: action, payload });
|
|
1880
|
+
},
|
|
1881
|
+
};
|
|
1882
|
+
// Use the injected factory to create an event manager with the component-specific action handler
|
|
1883
|
+
this.eventManager = this.eventManagerFactory(actionHandler);
|
|
1884
|
+
this.eventManager.attachHandlers(container);
|
|
1885
|
+
}
|
|
1886
|
+
handleClick(event) {
|
|
1887
|
+
const target = event.target;
|
|
1888
|
+
// Only trigger actions on non-form buttons and clickable elements (cards)
|
|
1889
|
+
// Don't trigger on input elements or form buttons (let WidgetEventManager handle those)
|
|
1890
|
+
const button = target.tagName === 'BUTTON' ? target : target.closest('button');
|
|
1891
|
+
if (button && !button.closest('[data-widget-type="form"]')) {
|
|
1892
|
+
const actionName = button.getAttribute('data-action');
|
|
1893
|
+
if (actionName) {
|
|
1894
|
+
try {
|
|
1895
|
+
const payloadStr = button.getAttribute('data-payload');
|
|
1896
|
+
const payload = payloadStr ? JSON.parse(payloadStr) : {};
|
|
1897
|
+
this.widgetAction.emit({ actionName, payload });
|
|
1898
|
+
}
|
|
1899
|
+
catch (err) {
|
|
1900
|
+
console.error('Failed to parse widget action payload:', err);
|
|
1901
|
+
}
|
|
1902
|
+
}
|
|
1903
|
+
}
|
|
1904
|
+
}
|
|
1905
|
+
/**
|
|
1906
|
+
* Cleanup all resources including event listeners.
|
|
1907
|
+
*/
|
|
1908
|
+
cleanup() {
|
|
1909
|
+
// Cleanup dynamic widgets first
|
|
1910
|
+
this.cleanupDynamicWidgets();
|
|
1911
|
+
// Cleanup event manager
|
|
1912
|
+
this.eventManager = undefined;
|
|
1913
|
+
}
|
|
1914
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.8", ngImport: i0, type: WidgetRendererComponent, deps: [{ token: SSR_WIDGET_RENDERER }, { token: ANGULAR_WIDGET_RENDERER, optional: true }, { token: WIDGET_EVENT_MANAGER_FACTORY }, { token: WidgetRegistryService }, { token: i0.Injector }, { token: i0.EnvironmentInjector }], target: i0.ɵɵFactoryTarget.Component });
|
|
1915
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.8", type: WidgetRendererComponent, isStandalone: true, selector: "bbq-widget-renderer", inputs: { widgets: "widgets" }, outputs: { widgetAction: "widgetAction" }, providers: [
|
|
1916
|
+
{ provide: WIDGET_EVENT_MANAGER_FACTORY, useFactory: widgetEventManagerFactoryProvider },
|
|
1917
|
+
{ provide: SSR_WIDGET_RENDERER, useFactory: ssrWidgetRendererFactory },
|
|
1918
|
+
{ provide: ANGULAR_WIDGET_RENDERER, useFactory: angularWidgetRendererFactory },
|
|
1919
|
+
], viewQueries: [{ propertyName: "containerRef", first: true, predicate: ["widgetContainer"], descendants: true }], usesOnChanges: true, ngImport: i0, template: `
|
|
1920
|
+
<div #widgetContainer class="bbq-widgets-container" (click)="handleClick($event)">
|
|
1921
|
+
@for (item of widgetItems; track item.index) {
|
|
1922
|
+
@if (item.isHtml) {
|
|
1923
|
+
<div class="bbq-widget" [innerHTML]="item.html"></div>
|
|
1924
|
+
} @else {
|
|
1925
|
+
<div class="bbq-widget" #dynamicWidget></div>
|
|
1926
|
+
}
|
|
1927
|
+
}
|
|
1928
|
+
</div>
|
|
1929
|
+
`, isInline: true, styles: [".bbq-widgets-container{margin-top:.5rem}.bbq-widget{margin-bottom:.5rem}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }] });
|
|
1930
|
+
}
|
|
1931
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.8", ngImport: i0, type: WidgetRendererComponent, decorators: [{
|
|
1932
|
+
type: Component,
|
|
1933
|
+
args: [{ selector: 'bbq-widget-renderer', standalone: true, imports: [CommonModule], providers: [
|
|
1934
|
+
{ provide: WIDGET_EVENT_MANAGER_FACTORY, useFactory: widgetEventManagerFactoryProvider },
|
|
1935
|
+
{ provide: SSR_WIDGET_RENDERER, useFactory: ssrWidgetRendererFactory },
|
|
1936
|
+
{ provide: ANGULAR_WIDGET_RENDERER, useFactory: angularWidgetRendererFactory },
|
|
1937
|
+
], template: `
|
|
1938
|
+
<div #widgetContainer class="bbq-widgets-container" (click)="handleClick($event)">
|
|
1939
|
+
@for (item of widgetItems; track item.index) {
|
|
1940
|
+
@if (item.isHtml) {
|
|
1941
|
+
<div class="bbq-widget" [innerHTML]="item.html"></div>
|
|
1942
|
+
} @else {
|
|
1943
|
+
<div class="bbq-widget" #dynamicWidget></div>
|
|
1944
|
+
}
|
|
1945
|
+
}
|
|
1946
|
+
</div>
|
|
1947
|
+
`, styles: [".bbq-widgets-container{margin-top:.5rem}.bbq-widget{margin-bottom:.5rem}\n"] }]
|
|
1948
|
+
}], ctorParameters: () => [{ type: i2$1.SsrWidgetRenderer, decorators: [{
|
|
1949
|
+
type: Inject,
|
|
1950
|
+
args: [SSR_WIDGET_RENDERER]
|
|
1951
|
+
}] }, { type: AngularWidgetRenderer, decorators: [{
|
|
1952
|
+
type: Optional
|
|
1953
|
+
}, {
|
|
1954
|
+
type: Inject,
|
|
1955
|
+
args: [ANGULAR_WIDGET_RENDERER]
|
|
1956
|
+
}] }, { type: undefined, decorators: [{
|
|
1957
|
+
type: Inject,
|
|
1958
|
+
args: [WIDGET_EVENT_MANAGER_FACTORY]
|
|
1959
|
+
}] }, { type: WidgetRegistryService }, { type: i0.Injector }, { type: i0.EnvironmentInjector }], propDecorators: { widgets: [{
|
|
1960
|
+
type: Input
|
|
1961
|
+
}], widgetAction: [{
|
|
1962
|
+
type: Output
|
|
1963
|
+
}], containerRef: [{
|
|
1964
|
+
type: ViewChild,
|
|
1965
|
+
args: ['widgetContainer', { static: false }]
|
|
1966
|
+
}] } });
|
|
1967
|
+
|
|
1968
|
+
/**
|
|
1969
|
+
* Angular widget renderers
|
|
1970
|
+
*/
|
|
1971
|
+
|
|
1972
|
+
/**
|
|
1973
|
+
* @bbq-chat/widgets-angular
|
|
1974
|
+
*
|
|
1975
|
+
* Angular components and services for BbQ ChatWidgets
|
|
1976
|
+
*
|
|
1977
|
+
* This package provides Angular-native components and services that wrap
|
|
1978
|
+
* the core @bbq-chat/widgets library, making it easy to integrate chat
|
|
1979
|
+
* widgets into Angular applications.
|
|
1980
|
+
*
|
|
1981
|
+
* @packageDocumentation
|
|
1982
|
+
*/
|
|
1983
|
+
// Export components
|
|
1984
|
+
// Version
|
|
1985
|
+
const VERSION = '1.0.4';
|
|
1986
|
+
|
|
1987
|
+
/**
|
|
1988
|
+
* Generated bundle index. Do not edit.
|
|
1989
|
+
*/
|
|
1990
|
+
|
|
1991
|
+
export { ANGULAR_WIDGET_RENDERER, AngularWidgetRenderer, BUILT_IN_WIDGET_COMPONENTS, ButtonWidgetComponent, CardWidgetComponent, DatePickerWidgetComponent, DropdownWidgetComponent, FileUploadWidgetComponent, FormWidgetComponent, ImageCollectionWidgetComponent, ImageWidgetComponent, InputWidgetComponent, MultiSelectWidgetComponent, ProgressBarWidgetComponent, SSR_WIDGET_RENDERER, SliderWidgetComponent, TextAreaWidgetComponent, ThemeSwitcherWidgetComponent, ToggleWidgetComponent, VERSION, WIDGET_EVENT_MANAGER_FACTORY, WidgetRegistryService, WidgetRendererComponent, angularWidgetRendererFactory, isComponentRenderer, isHtmlRenderer, isTemplateRenderer, ssrWidgetRendererFactory, widgetEventManagerFactoryProvider };
|
|
1992
|
+
//# sourceMappingURL=index.mjs.map
|