@bbq-chat/widgets-angular 1.0.4 → 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 +92 -3
- package/dist/fesm2022/index.mjs +1468 -18
- package/dist/fesm2022/index.mjs.map +1 -1
- package/dist/types/index.d.ts +290 -7
- package/package.json +1 -1
package/dist/fesm2022/index.mjs
CHANGED
|
@@ -1,9 +1,12 @@
|
|
|
1
1
|
import * as i0 from '@angular/core';
|
|
2
|
-
import { InjectionToken, Injectable, EventEmitter,
|
|
2
|
+
import { Input, Component, createComponent, ViewContainerRef, ViewChildren, InjectionToken, Injectable, EventEmitter, ViewChild, Output, Inject, Optional } from '@angular/core';
|
|
3
|
+
import * as i1 from '@angular/common';
|
|
3
4
|
import { CommonModule } from '@angular/common';
|
|
4
|
-
import * as i2 from '@bbq-chat/widgets';
|
|
5
|
+
import * as i2$1 from '@bbq-chat/widgets';
|
|
5
6
|
import { WidgetEventManager, SsrWidgetRenderer, customWidgetRegistry } from '@bbq-chat/widgets';
|
|
6
7
|
export { ChatWidget, SsrWidgetRenderer, WidgetEventManager, customWidgetRegistry } from '@bbq-chat/widgets';
|
|
8
|
+
import * as i2 from '@angular/forms';
|
|
9
|
+
import { FormsModule } from '@angular/forms';
|
|
7
10
|
|
|
8
11
|
/**
|
|
9
12
|
* Type guard to check if a renderer is a TemplateRef
|
|
@@ -48,6 +51,1391 @@ function isHtmlRenderer(renderer) {
|
|
|
48
51
|
return typeof renderer === 'function';
|
|
49
52
|
}
|
|
50
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
|
+
|
|
51
1439
|
const WIDGET_EVENT_MANAGER_FACTORY = new InjectionToken('WIDGET_EVENT_MANAGER_FACTORY');
|
|
52
1440
|
/**
|
|
53
1441
|
* Injection token for SsrWidgetRenderer
|
|
@@ -62,6 +1450,19 @@ const WIDGET_EVENT_MANAGER_FACTORY = new InjectionToken('WIDGET_EVENT_MANAGER_FA
|
|
|
62
1450
|
* ```
|
|
63
1451
|
*/
|
|
64
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');
|
|
65
1466
|
/**
|
|
66
1467
|
* Factory function for creating WidgetEventManager instances
|
|
67
1468
|
*
|
|
@@ -85,6 +1486,19 @@ function widgetEventManagerFactoryProvider() {
|
|
|
85
1486
|
function ssrWidgetRendererFactory() {
|
|
86
1487
|
return new SsrWidgetRenderer();
|
|
87
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
|
+
}
|
|
88
1502
|
|
|
89
1503
|
/**
|
|
90
1504
|
* Service for registering custom widget factories and renderers
|
|
@@ -228,6 +1642,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.8", ngImpor
|
|
|
228
1642
|
*/
|
|
229
1643
|
class WidgetRendererComponent {
|
|
230
1644
|
renderer;
|
|
1645
|
+
angularRenderer;
|
|
231
1646
|
eventManagerFactory;
|
|
232
1647
|
widgetRegistry;
|
|
233
1648
|
injector;
|
|
@@ -246,8 +1661,9 @@ class WidgetRendererComponent {
|
|
|
246
1661
|
isViewInitialized = false;
|
|
247
1662
|
dynamicComponents = [];
|
|
248
1663
|
dynamicViews = [];
|
|
249
|
-
constructor(renderer, eventManagerFactory, widgetRegistry, injector, environmentInjector) {
|
|
1664
|
+
constructor(renderer, angularRenderer, eventManagerFactory, widgetRegistry, injector, environmentInjector) {
|
|
250
1665
|
this.renderer = renderer;
|
|
1666
|
+
this.angularRenderer = angularRenderer;
|
|
251
1667
|
this.eventManagerFactory = eventManagerFactory;
|
|
252
1668
|
this.widgetRegistry = widgetRegistry;
|
|
253
1669
|
this.injector = injector;
|
|
@@ -312,7 +1728,18 @@ class WidgetRendererComponent {
|
|
|
312
1728
|
html: customRenderer(widget),
|
|
313
1729
|
};
|
|
314
1730
|
}
|
|
315
|
-
//
|
|
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
|
|
316
1743
|
return {
|
|
317
1744
|
index,
|
|
318
1745
|
widget,
|
|
@@ -347,18 +1774,26 @@ class WidgetRendererComponent {
|
|
|
347
1774
|
this.widgetItems.forEach((item) => {
|
|
348
1775
|
if (!item.isHtml) {
|
|
349
1776
|
const customRenderer = this.widgetRegistry.getRenderer(item.widget.type);
|
|
350
|
-
if (!customRenderer)
|
|
351
|
-
return;
|
|
352
1777
|
const targetDiv = dynamicWidgetDivs[dynamicIndex];
|
|
353
1778
|
if (!targetDiv)
|
|
354
1779
|
return;
|
|
355
1780
|
// Clear the div content before rendering
|
|
356
1781
|
targetDiv.innerHTML = '';
|
|
357
|
-
|
|
358
|
-
|
|
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
|
+
}
|
|
359
1790
|
}
|
|
360
|
-
else if (
|
|
361
|
-
|
|
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
|
+
}
|
|
362
1797
|
}
|
|
363
1798
|
dynamicIndex++;
|
|
364
1799
|
}
|
|
@@ -382,11 +1817,15 @@ class WidgetRendererComponent {
|
|
|
382
1817
|
// Safely set component inputs if they exist
|
|
383
1818
|
const instance = componentRef.instance;
|
|
384
1819
|
// Set widget property if it exists in the prototype chain
|
|
385
|
-
instance['widget']
|
|
1820
|
+
if (!instance['widget']) {
|
|
1821
|
+
instance['widget'] = widget;
|
|
1822
|
+
}
|
|
386
1823
|
// Set widgetAction property if it exists in the prototype chain
|
|
387
|
-
instance['widgetAction']
|
|
388
|
-
|
|
389
|
-
|
|
1824
|
+
if (!instance['widgetAction']) {
|
|
1825
|
+
instance['widgetAction'] = (actionName, payload) => {
|
|
1826
|
+
this.widgetAction.emit({ actionName, payload });
|
|
1827
|
+
};
|
|
1828
|
+
}
|
|
390
1829
|
// Attach the component's host view to the target element
|
|
391
1830
|
targetElement.appendChild(componentRef.location.nativeElement);
|
|
392
1831
|
// Store reference for cleanup
|
|
@@ -472,10 +1911,11 @@ class WidgetRendererComponent {
|
|
|
472
1911
|
// Cleanup event manager
|
|
473
1912
|
this.eventManager = undefined;
|
|
474
1913
|
}
|
|
475
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.8", ngImport: i0, type: WidgetRendererComponent, deps: [{ token: SSR_WIDGET_RENDERER }, { token: WIDGET_EVENT_MANAGER_FACTORY }, { token: WidgetRegistryService }, { token: i0.Injector }, { token: i0.EnvironmentInjector }], target: i0.ɵɵFactoryTarget.Component });
|
|
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 });
|
|
476
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: [
|
|
477
1916
|
{ provide: WIDGET_EVENT_MANAGER_FACTORY, useFactory: widgetEventManagerFactoryProvider },
|
|
478
1917
|
{ provide: SSR_WIDGET_RENDERER, useFactory: ssrWidgetRendererFactory },
|
|
1918
|
+
{ provide: ANGULAR_WIDGET_RENDERER, useFactory: angularWidgetRendererFactory },
|
|
479
1919
|
], viewQueries: [{ propertyName: "containerRef", first: true, predicate: ["widgetContainer"], descendants: true }], usesOnChanges: true, ngImport: i0, template: `
|
|
480
1920
|
<div #widgetContainer class="bbq-widgets-container" (click)="handleClick($event)">
|
|
481
1921
|
@for (item of widgetItems; track item.index) {
|
|
@@ -493,6 +1933,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.8", ngImpor
|
|
|
493
1933
|
args: [{ selector: 'bbq-widget-renderer', standalone: true, imports: [CommonModule], providers: [
|
|
494
1934
|
{ provide: WIDGET_EVENT_MANAGER_FACTORY, useFactory: widgetEventManagerFactoryProvider },
|
|
495
1935
|
{ provide: SSR_WIDGET_RENDERER, useFactory: ssrWidgetRendererFactory },
|
|
1936
|
+
{ provide: ANGULAR_WIDGET_RENDERER, useFactory: angularWidgetRendererFactory },
|
|
496
1937
|
], template: `
|
|
497
1938
|
<div #widgetContainer class="bbq-widgets-container" (click)="handleClick($event)">
|
|
498
1939
|
@for (item of widgetItems; track item.index) {
|
|
@@ -504,9 +1945,14 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.8", ngImpor
|
|
|
504
1945
|
}
|
|
505
1946
|
</div>
|
|
506
1947
|
`, styles: [".bbq-widgets-container{margin-top:.5rem}.bbq-widget{margin-bottom:.5rem}\n"] }]
|
|
507
|
-
}], ctorParameters: () => [{ type: i2.SsrWidgetRenderer, decorators: [{
|
|
1948
|
+
}], ctorParameters: () => [{ type: i2$1.SsrWidgetRenderer, decorators: [{
|
|
508
1949
|
type: Inject,
|
|
509
1950
|
args: [SSR_WIDGET_RENDERER]
|
|
1951
|
+
}] }, { type: AngularWidgetRenderer, decorators: [{
|
|
1952
|
+
type: Optional
|
|
1953
|
+
}, {
|
|
1954
|
+
type: Inject,
|
|
1955
|
+
args: [ANGULAR_WIDGET_RENDERER]
|
|
510
1956
|
}] }, { type: undefined, decorators: [{
|
|
511
1957
|
type: Inject,
|
|
512
1958
|
args: [WIDGET_EVENT_MANAGER_FACTORY]
|
|
@@ -519,6 +1965,10 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.8", ngImpor
|
|
|
519
1965
|
args: ['widgetContainer', { static: false }]
|
|
520
1966
|
}] } });
|
|
521
1967
|
|
|
1968
|
+
/**
|
|
1969
|
+
* Angular widget renderers
|
|
1970
|
+
*/
|
|
1971
|
+
|
|
522
1972
|
/**
|
|
523
1973
|
* @bbq-chat/widgets-angular
|
|
524
1974
|
*
|
|
@@ -532,11 +1982,11 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.8", ngImpor
|
|
|
532
1982
|
*/
|
|
533
1983
|
// Export components
|
|
534
1984
|
// Version
|
|
535
|
-
const VERSION = '1.0.
|
|
1985
|
+
const VERSION = '1.0.4';
|
|
536
1986
|
|
|
537
1987
|
/**
|
|
538
1988
|
* Generated bundle index. Do not edit.
|
|
539
1989
|
*/
|
|
540
1990
|
|
|
541
|
-
export { SSR_WIDGET_RENDERER, VERSION, WIDGET_EVENT_MANAGER_FACTORY, WidgetRegistryService, WidgetRendererComponent, isComponentRenderer, isHtmlRenderer, isTemplateRenderer, ssrWidgetRendererFactory, widgetEventManagerFactoryProvider };
|
|
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 };
|
|
542
1992
|
//# sourceMappingURL=index.mjs.map
|