@ngstarter-ui/components 21.0.46 → 21.0.47
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/ai/component-registry.json +12 -1
- package/fesm2022/ngstarter-ui-components-card.mjs +2 -2
- package/fesm2022/ngstarter-ui-components-form-builder.mjs +977 -284
- package/fesm2022/ngstarter-ui-components-form-builder.mjs.map +1 -1
- package/package.json +1 -1
- package/styles/_common.scss +1 -0
- package/types/ngstarter-ui-components-form-builder.d.ts +43 -12
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import * as i0 from '@angular/core';
|
|
2
|
-
import { InjectionToken, makeEnvironmentProviders, inject, input, signal, computed, viewChild, ViewContainerRef, effect, ChangeDetectionStrategy, Component, model, output,
|
|
2
|
+
import { InjectionToken, makeEnvironmentProviders, inject, input, signal, computed, viewChild, ViewContainerRef, effect, ChangeDetectionStrategy, Component, model, output, ElementRef, DestroyRef } from '@angular/core';
|
|
3
3
|
import * as i1 from '@angular/forms';
|
|
4
|
-
import { Validators, ReactiveFormsModule, FormControl, FormGroup, FormsModule } from '@angular/forms';
|
|
4
|
+
import { Validators, ReactiveFormsModule, FormControl, FormArray, FormGroup, FormsModule } from '@angular/forms';
|
|
5
5
|
import { NgTemplateOutlet } from '@angular/common';
|
|
6
6
|
import { Button } from '@ngstarter-ui/components/button';
|
|
7
7
|
import { Card, CardAside, CardContent, CardHeader } from '@ngstarter-ui/components/card';
|
|
@@ -41,6 +41,16 @@ function formBuilderItem(definition) {
|
|
|
41
41
|
function formBuilderSettings(definition) {
|
|
42
42
|
return definition;
|
|
43
43
|
}
|
|
44
|
+
function provideFormBuilderField(definition) {
|
|
45
|
+
return {
|
|
46
|
+
provide: FORM_BUILDER_FIELDS,
|
|
47
|
+
useValue: definition,
|
|
48
|
+
multi: true
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
function provideFormBuilderFields(definitions) {
|
|
52
|
+
return definitions.map(definition => provideFormBuilderField(definition));
|
|
53
|
+
}
|
|
44
54
|
function provideFormBuilder(config = {}) {
|
|
45
55
|
return makeEnvironmentProviders([
|
|
46
56
|
...(config.uploadCallback ? [{
|
|
@@ -64,6 +74,130 @@ function provideFormBuilder(config = {}) {
|
|
|
64
74
|
}))
|
|
65
75
|
]);
|
|
66
76
|
}
|
|
77
|
+
const FIELD_WIDTH_OPTIONS = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]
|
|
78
|
+
.map(width => ({ label: `${width}/12`, value: width }));
|
|
79
|
+
const SPACER_HEIGHT_OPTIONS = [8, 16, 24, 32, 48, 64]
|
|
80
|
+
.map(height => ({ label: `${height}px`, value: height }));
|
|
81
|
+
const ORIENTATION_OPTIONS = [
|
|
82
|
+
{ label: 'Vertical', value: 'vertical' },
|
|
83
|
+
{ label: 'Horizontal', value: 'horizontal' }
|
|
84
|
+
];
|
|
85
|
+
const FORM_BUILDER_FIELD_BASE_SETTINGS_SCHEMA = {
|
|
86
|
+
sections: [
|
|
87
|
+
{
|
|
88
|
+
id: 'field-base-settings',
|
|
89
|
+
title: 'Field',
|
|
90
|
+
fields: [
|
|
91
|
+
{ id: 'field-label', name: 'label', type: 'text', label: 'Label' },
|
|
92
|
+
{ id: 'field-name', name: 'name', type: 'text', label: 'Field ID' },
|
|
93
|
+
{ id: 'field-hint', name: 'hint', type: 'text', label: 'Hint' },
|
|
94
|
+
{ id: 'field-width', name: 'width', type: 'select', label: 'Width', defaultValue: 12, options: FIELD_WIDTH_OPTIONS },
|
|
95
|
+
{ id: 'field-required', name: 'required', type: 'toggle', label: 'Required field', defaultValue: false },
|
|
96
|
+
{ id: 'field-readonly', name: 'readonly', type: 'toggle', label: 'Readonly', defaultValue: false },
|
|
97
|
+
{ id: 'field-disabled', name: 'disabled', type: 'toggle', label: 'Disabled', defaultValue: false }
|
|
98
|
+
]
|
|
99
|
+
}
|
|
100
|
+
]
|
|
101
|
+
};
|
|
102
|
+
const FORM_BUILDER_INPUT_FIELD_BASE_SETTINGS_SCHEMA = {
|
|
103
|
+
sections: [
|
|
104
|
+
{
|
|
105
|
+
id: 'input-field-base-settings',
|
|
106
|
+
title: 'Field',
|
|
107
|
+
fields: [
|
|
108
|
+
{ id: 'input-field-label', name: 'label', type: 'text', label: 'Label' },
|
|
109
|
+
{ id: 'input-field-name', name: 'name', type: 'text', label: 'Field ID' },
|
|
110
|
+
{ id: 'input-field-placeholder', name: 'placeholder', type: 'text', label: 'Placeholder' },
|
|
111
|
+
{ id: 'input-field-hint', name: 'hint', type: 'text', label: 'Hint' },
|
|
112
|
+
{ id: 'input-field-width', name: 'width', type: 'select', label: 'Width', defaultValue: 12, options: FIELD_WIDTH_OPTIONS },
|
|
113
|
+
{ id: 'input-field-required', name: 'required', type: 'toggle', label: 'Required field', defaultValue: false },
|
|
114
|
+
{ id: 'input-field-readonly', name: 'readonly', type: 'toggle', label: 'Readonly', defaultValue: false },
|
|
115
|
+
{ id: 'input-field-disabled', name: 'disabled', type: 'toggle', label: 'Disabled', defaultValue: false }
|
|
116
|
+
]
|
|
117
|
+
}
|
|
118
|
+
]
|
|
119
|
+
};
|
|
120
|
+
const FORM_BUILDER_LAYOUT_BASE_SETTINGS_SCHEMA = {
|
|
121
|
+
sections: [
|
|
122
|
+
{
|
|
123
|
+
id: 'layout-base-settings',
|
|
124
|
+
title: 'Layout',
|
|
125
|
+
fields: [
|
|
126
|
+
{ id: 'layout-label', name: 'label', type: 'text', label: 'Label' },
|
|
127
|
+
{ id: 'layout-hint', name: 'hint', type: 'text', label: 'Hint' },
|
|
128
|
+
{ id: 'layout-width', name: 'width', type: 'select', label: 'Width', defaultValue: 12, options: FIELD_WIDTH_OPTIONS }
|
|
129
|
+
]
|
|
130
|
+
}
|
|
131
|
+
]
|
|
132
|
+
};
|
|
133
|
+
const FORM_BUILDER_LAYOUT_CONTAINER_BASE_SETTINGS_SCHEMA = {
|
|
134
|
+
sections: [
|
|
135
|
+
{
|
|
136
|
+
id: 'layout-container-base-settings',
|
|
137
|
+
title: 'Layout',
|
|
138
|
+
fields: [
|
|
139
|
+
{ id: 'layout-container-label', name: 'label', type: 'text', label: 'Label' },
|
|
140
|
+
{ id: 'layout-container-width', name: 'width', type: 'select', label: 'Width', defaultValue: 12, options: FIELD_WIDTH_OPTIONS }
|
|
141
|
+
]
|
|
142
|
+
}
|
|
143
|
+
]
|
|
144
|
+
};
|
|
145
|
+
const FORM_BUILDER_STATIC_BASE_SETTINGS_SCHEMA = {
|
|
146
|
+
sections: [
|
|
147
|
+
{
|
|
148
|
+
id: 'static-base-settings',
|
|
149
|
+
title: 'Static block',
|
|
150
|
+
fields: [
|
|
151
|
+
{ id: 'static-label', name: 'label', type: 'text', label: 'Label' },
|
|
152
|
+
{ id: 'static-width', name: 'width', type: 'select', label: 'Width', defaultValue: 12, options: FIELD_WIDTH_OPTIONS }
|
|
153
|
+
]
|
|
154
|
+
}
|
|
155
|
+
]
|
|
156
|
+
};
|
|
157
|
+
const FORM_BUILDER_SECTION_BASE_SETTINGS_SCHEMA = {
|
|
158
|
+
sections: [
|
|
159
|
+
{
|
|
160
|
+
id: 'section-base-settings',
|
|
161
|
+
title: 'Section',
|
|
162
|
+
fields: [
|
|
163
|
+
{ id: 'section-title', name: 'title', type: 'text', label: 'Title' },
|
|
164
|
+
{ id: 'section-description', name: 'description', type: 'textarea', label: 'Description', hint: 'Optional helper text rendered under the section title.' },
|
|
165
|
+
{ id: 'section-collapsed', name: 'collapsed', type: 'toggle', label: 'Collapsed', defaultValue: false }
|
|
166
|
+
]
|
|
167
|
+
}
|
|
168
|
+
]
|
|
169
|
+
};
|
|
170
|
+
const CHECKED_SETTINGS_SCHEMA = {
|
|
171
|
+
sections: [
|
|
172
|
+
{
|
|
173
|
+
id: 'checked-settings',
|
|
174
|
+
title: 'Default state',
|
|
175
|
+
fields: [
|
|
176
|
+
{ id: 'default-checked', name: 'defaultValue', type: 'toggle', label: 'Checked', defaultValue: false }
|
|
177
|
+
]
|
|
178
|
+
}
|
|
179
|
+
]
|
|
180
|
+
};
|
|
181
|
+
const OPTIONS_SETTINGS_SCHEMA = {
|
|
182
|
+
sections: [
|
|
183
|
+
{
|
|
184
|
+
id: 'options-settings',
|
|
185
|
+
title: 'Options',
|
|
186
|
+
fields: [
|
|
187
|
+
{
|
|
188
|
+
id: 'options',
|
|
189
|
+
name: 'options',
|
|
190
|
+
type: 'textarea',
|
|
191
|
+
label: 'Options',
|
|
192
|
+
hint: 'One option per line. Use Label:value or Label:value:selected.',
|
|
193
|
+
settings: {
|
|
194
|
+
valueAdapter: 'optionsText'
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
]
|
|
198
|
+
}
|
|
199
|
+
]
|
|
200
|
+
};
|
|
67
201
|
const DEFAULT_FORM_BUILDER_FIELDS = [
|
|
68
202
|
{
|
|
69
203
|
type: 'text',
|
|
@@ -73,6 +207,9 @@ const DEFAULT_FORM_BUILDER_FIELDS = [
|
|
|
73
207
|
defaults: {
|
|
74
208
|
label: 'Text field',
|
|
75
209
|
placeholder: 'Enter text'
|
|
210
|
+
},
|
|
211
|
+
settings: {
|
|
212
|
+
extends: 'input-field'
|
|
76
213
|
}
|
|
77
214
|
},
|
|
78
215
|
{
|
|
@@ -83,6 +220,9 @@ const DEFAULT_FORM_BUILDER_FIELDS = [
|
|
|
83
220
|
defaults: {
|
|
84
221
|
label: 'Number',
|
|
85
222
|
placeholder: '0'
|
|
223
|
+
},
|
|
224
|
+
settings: {
|
|
225
|
+
extends: 'input-field'
|
|
86
226
|
}
|
|
87
227
|
},
|
|
88
228
|
{
|
|
@@ -94,6 +234,9 @@ const DEFAULT_FORM_BUILDER_FIELDS = [
|
|
|
94
234
|
label: 'Email',
|
|
95
235
|
placeholder: 'name@example.com'
|
|
96
236
|
},
|
|
237
|
+
settings: {
|
|
238
|
+
extends: 'input-field'
|
|
239
|
+
},
|
|
97
240
|
validators: field => field.required ? [Validators.required, Validators.email] : [Validators.email]
|
|
98
241
|
},
|
|
99
242
|
{
|
|
@@ -105,6 +248,9 @@ const DEFAULT_FORM_BUILDER_FIELDS = [
|
|
|
105
248
|
label: 'Description',
|
|
106
249
|
placeholder: 'Enter description',
|
|
107
250
|
width: 12
|
|
251
|
+
},
|
|
252
|
+
settings: {
|
|
253
|
+
extends: 'input-field'
|
|
108
254
|
}
|
|
109
255
|
},
|
|
110
256
|
{
|
|
@@ -120,6 +266,56 @@ const DEFAULT_FORM_BUILDER_FIELDS = [
|
|
|
120
266
|
label: 'Group',
|
|
121
267
|
width: 12,
|
|
122
268
|
children: []
|
|
269
|
+
},
|
|
270
|
+
settings: {
|
|
271
|
+
extends: 'layout-container'
|
|
272
|
+
}
|
|
273
|
+
},
|
|
274
|
+
{
|
|
275
|
+
type: 'repeater',
|
|
276
|
+
label: 'Repeater',
|
|
277
|
+
kind: 'layout',
|
|
278
|
+
group: 'Layout',
|
|
279
|
+
icon: 'fluent:copy-add-24-regular',
|
|
280
|
+
description: 'Repeat a group of nested fields as a FormArray.',
|
|
281
|
+
acceptsChildren: true,
|
|
282
|
+
defaults: {
|
|
283
|
+
kind: 'layout',
|
|
284
|
+
label: 'Repeater',
|
|
285
|
+
name: 'items',
|
|
286
|
+
width: 12,
|
|
287
|
+
settings: {
|
|
288
|
+
allowNullValue: false,
|
|
289
|
+
emptyText: 'No items added yet.'
|
|
290
|
+
},
|
|
291
|
+
children: []
|
|
292
|
+
},
|
|
293
|
+
settings: {
|
|
294
|
+
extends: 'layout-container',
|
|
295
|
+
schema: {
|
|
296
|
+
sections: [
|
|
297
|
+
{
|
|
298
|
+
id: 'repeater-settings',
|
|
299
|
+
title: 'Repeater',
|
|
300
|
+
fields: [
|
|
301
|
+
{
|
|
302
|
+
id: 'repeater-allow-null-value',
|
|
303
|
+
name: 'settings.allowNullValue',
|
|
304
|
+
type: 'toggle',
|
|
305
|
+
label: 'Allow null value',
|
|
306
|
+
defaultValue: false
|
|
307
|
+
},
|
|
308
|
+
{
|
|
309
|
+
id: 'repeater-empty-text',
|
|
310
|
+
name: 'settings.emptyText',
|
|
311
|
+
type: 'textarea',
|
|
312
|
+
label: 'Empty text',
|
|
313
|
+
defaultValue: 'No items added yet.'
|
|
314
|
+
}
|
|
315
|
+
]
|
|
316
|
+
}
|
|
317
|
+
]
|
|
318
|
+
}
|
|
123
319
|
}
|
|
124
320
|
},
|
|
125
321
|
{
|
|
@@ -136,6 +332,20 @@ const DEFAULT_FORM_BUILDER_FIELDS = [
|
|
|
136
332
|
settings: {
|
|
137
333
|
height: 24
|
|
138
334
|
}
|
|
335
|
+
},
|
|
336
|
+
settings: {
|
|
337
|
+
extends: 'static',
|
|
338
|
+
schema: {
|
|
339
|
+
sections: [
|
|
340
|
+
{
|
|
341
|
+
id: 'spacer-settings',
|
|
342
|
+
title: 'Spacer',
|
|
343
|
+
fields: [
|
|
344
|
+
{ id: 'spacer-height', name: 'settings.height', type: 'select', label: 'Height', defaultValue: 24, options: SPACER_HEIGHT_OPTIONS }
|
|
345
|
+
]
|
|
346
|
+
}
|
|
347
|
+
]
|
|
348
|
+
}
|
|
139
349
|
}
|
|
140
350
|
},
|
|
141
351
|
{
|
|
@@ -151,6 +361,31 @@ const DEFAULT_FORM_BUILDER_FIELDS = [
|
|
|
151
361
|
{ label: 'Option 1', value: 'option_1' },
|
|
152
362
|
{ label: 'Option 2', value: 'option_2' }
|
|
153
363
|
]
|
|
364
|
+
},
|
|
365
|
+
settings: {
|
|
366
|
+
extends: 'input-field',
|
|
367
|
+
schema: {
|
|
368
|
+
sections: [
|
|
369
|
+
{
|
|
370
|
+
id: 'select-behavior-settings',
|
|
371
|
+
title: 'Select',
|
|
372
|
+
fields: [
|
|
373
|
+
{
|
|
374
|
+
id: 'select-multiple',
|
|
375
|
+
name: 'multiple',
|
|
376
|
+
type: 'toggle',
|
|
377
|
+
label: 'Multiple',
|
|
378
|
+
defaultValue: false,
|
|
379
|
+
settings: {
|
|
380
|
+
valueAdapter: 'selectMultiple'
|
|
381
|
+
}
|
|
382
|
+
},
|
|
383
|
+
{ id: 'select-clearable', name: 'clearable', type: 'toggle', label: 'Clearable', defaultValue: true }
|
|
384
|
+
]
|
|
385
|
+
},
|
|
386
|
+
...OPTIONS_SETTINGS_SCHEMA.sections
|
|
387
|
+
]
|
|
388
|
+
}
|
|
154
389
|
}
|
|
155
390
|
},
|
|
156
391
|
{
|
|
@@ -167,6 +402,21 @@ const DEFAULT_FORM_BUILDER_FIELDS = [
|
|
|
167
402
|
{ label: 'Option 1', value: 'option_1' },
|
|
168
403
|
{ label: 'Option 2', value: 'option_2' }
|
|
169
404
|
]
|
|
405
|
+
},
|
|
406
|
+
settings: {
|
|
407
|
+
extends: 'field',
|
|
408
|
+
schema: {
|
|
409
|
+
sections: [
|
|
410
|
+
{
|
|
411
|
+
id: 'radio-settings',
|
|
412
|
+
title: 'Radio',
|
|
413
|
+
fields: [
|
|
414
|
+
{ id: 'radio-orientation', name: 'settings.orientation', type: 'select', label: 'Orientation', defaultValue: 'vertical', options: ORIENTATION_OPTIONS }
|
|
415
|
+
]
|
|
416
|
+
},
|
|
417
|
+
...OPTIONS_SETTINGS_SCHEMA.sections
|
|
418
|
+
]
|
|
419
|
+
}
|
|
170
420
|
}
|
|
171
421
|
},
|
|
172
422
|
{
|
|
@@ -177,6 +427,10 @@ const DEFAULT_FORM_BUILDER_FIELDS = [
|
|
|
177
427
|
defaults: {
|
|
178
428
|
label: 'Checkbox',
|
|
179
429
|
defaultValue: false
|
|
430
|
+
},
|
|
431
|
+
settings: {
|
|
432
|
+
extends: 'field',
|
|
433
|
+
schema: CHECKED_SETTINGS_SCHEMA
|
|
180
434
|
}
|
|
181
435
|
},
|
|
182
436
|
{
|
|
@@ -187,6 +441,10 @@ const DEFAULT_FORM_BUILDER_FIELDS = [
|
|
|
187
441
|
defaults: {
|
|
188
442
|
label: 'Toggle',
|
|
189
443
|
defaultValue: false
|
|
444
|
+
},
|
|
445
|
+
settings: {
|
|
446
|
+
extends: 'field',
|
|
447
|
+
schema: CHECKED_SETTINGS_SCHEMA
|
|
190
448
|
}
|
|
191
449
|
},
|
|
192
450
|
{
|
|
@@ -197,6 +455,9 @@ const DEFAULT_FORM_BUILDER_FIELDS = [
|
|
|
197
455
|
defaults: {
|
|
198
456
|
label: 'Date',
|
|
199
457
|
placeholder: 'Select date'
|
|
458
|
+
},
|
|
459
|
+
settings: {
|
|
460
|
+
extends: 'input-field'
|
|
200
461
|
}
|
|
201
462
|
},
|
|
202
463
|
{
|
|
@@ -207,6 +468,9 @@ const DEFAULT_FORM_BUILDER_FIELDS = [
|
|
|
207
468
|
defaults: {
|
|
208
469
|
label: 'Time',
|
|
209
470
|
placeholder: 'Select time'
|
|
471
|
+
},
|
|
472
|
+
settings: {
|
|
473
|
+
extends: 'input-field'
|
|
210
474
|
}
|
|
211
475
|
},
|
|
212
476
|
{
|
|
@@ -218,6 +482,9 @@ const DEFAULT_FORM_BUILDER_FIELDS = [
|
|
|
218
482
|
label: 'Date range',
|
|
219
483
|
placeholder: 'Start date',
|
|
220
484
|
width: 12
|
|
485
|
+
},
|
|
486
|
+
settings: {
|
|
487
|
+
extends: 'input-field'
|
|
221
488
|
}
|
|
222
489
|
},
|
|
223
490
|
{
|
|
@@ -233,6 +500,37 @@ const DEFAULT_FORM_BUILDER_FIELDS = [
|
|
|
233
500
|
settings: {
|
|
234
501
|
accept: '*/*'
|
|
235
502
|
}
|
|
503
|
+
},
|
|
504
|
+
settings: {
|
|
505
|
+
extends: 'input-field',
|
|
506
|
+
schema: {
|
|
507
|
+
sections: [
|
|
508
|
+
{
|
|
509
|
+
id: 'upload-settings',
|
|
510
|
+
title: 'Upload',
|
|
511
|
+
fields: [
|
|
512
|
+
{
|
|
513
|
+
id: 'upload-multiple',
|
|
514
|
+
name: 'multiple',
|
|
515
|
+
type: 'toggle',
|
|
516
|
+
label: 'Multiple',
|
|
517
|
+
defaultValue: false,
|
|
518
|
+
settings: {
|
|
519
|
+
valueAdapter: 'multipleDefaultValue'
|
|
520
|
+
}
|
|
521
|
+
},
|
|
522
|
+
{
|
|
523
|
+
id: 'upload-accept',
|
|
524
|
+
name: 'settings.accept',
|
|
525
|
+
type: 'text',
|
|
526
|
+
label: 'Accepted file types',
|
|
527
|
+
defaultValue: '*/*',
|
|
528
|
+
hint: 'Use MIME types separated by commas, for example image/*,application/pdf.'
|
|
529
|
+
}
|
|
530
|
+
]
|
|
531
|
+
}
|
|
532
|
+
]
|
|
533
|
+
}
|
|
236
534
|
}
|
|
237
535
|
},
|
|
238
536
|
{
|
|
@@ -243,6 +541,9 @@ const DEFAULT_FORM_BUILDER_FIELDS = [
|
|
|
243
541
|
defaults: {
|
|
244
542
|
label: 'Timezone',
|
|
245
543
|
placeholder: 'Select timezone'
|
|
544
|
+
},
|
|
545
|
+
settings: {
|
|
546
|
+
extends: 'input-field'
|
|
246
547
|
}
|
|
247
548
|
},
|
|
248
549
|
{
|
|
@@ -253,6 +554,9 @@ const DEFAULT_FORM_BUILDER_FIELDS = [
|
|
|
253
554
|
defaults: {
|
|
254
555
|
label: 'Amount',
|
|
255
556
|
placeholder: '0.00'
|
|
557
|
+
},
|
|
558
|
+
settings: {
|
|
559
|
+
extends: 'input-field'
|
|
256
560
|
}
|
|
257
561
|
},
|
|
258
562
|
{
|
|
@@ -263,6 +567,9 @@ const DEFAULT_FORM_BUILDER_FIELDS = [
|
|
|
263
567
|
defaults: {
|
|
264
568
|
label: 'Currency',
|
|
265
569
|
placeholder: 'Select currency'
|
|
570
|
+
},
|
|
571
|
+
settings: {
|
|
572
|
+
extends: 'input-field'
|
|
266
573
|
}
|
|
267
574
|
},
|
|
268
575
|
{
|
|
@@ -274,6 +581,20 @@ const DEFAULT_FORM_BUILDER_FIELDS = [
|
|
|
274
581
|
label: 'Country',
|
|
275
582
|
placeholder: 'Select country',
|
|
276
583
|
clearable: true
|
|
584
|
+
},
|
|
585
|
+
settings: {
|
|
586
|
+
extends: 'input-field',
|
|
587
|
+
schema: {
|
|
588
|
+
sections: [
|
|
589
|
+
{
|
|
590
|
+
id: 'country-select-settings',
|
|
591
|
+
title: 'Country select',
|
|
592
|
+
fields: [
|
|
593
|
+
{ id: 'country-clearable', name: 'clearable', type: 'toggle', label: 'Clearable', defaultValue: true }
|
|
594
|
+
]
|
|
595
|
+
}
|
|
596
|
+
]
|
|
597
|
+
}
|
|
277
598
|
}
|
|
278
599
|
}
|
|
279
600
|
];
|
|
@@ -285,7 +606,11 @@ const DEFAULT_FORM_BUILDER_ITEMS = [
|
|
|
285
606
|
group: 'Layout',
|
|
286
607
|
icon: 'fluent:folder-24-regular',
|
|
287
608
|
description: 'Top-level layout container.',
|
|
288
|
-
acceptsChildren: true
|
|
609
|
+
acceptsChildren: true,
|
|
610
|
+
settings: {
|
|
611
|
+
extends: 'none',
|
|
612
|
+
schema: FORM_BUILDER_SECTION_BASE_SETTINGS_SCHEMA
|
|
613
|
+
}
|
|
289
614
|
},
|
|
290
615
|
...DEFAULT_FORM_BUILDER_FIELDS
|
|
291
616
|
];
|
|
@@ -551,8 +876,8 @@ class FormBuilderRenderer {
|
|
|
551
876
|
onCleanup(() => subscription.unsubscribe());
|
|
552
877
|
});
|
|
553
878
|
}
|
|
554
|
-
getControl(field) {
|
|
555
|
-
const control =
|
|
879
|
+
getControl(field, formGroup = this.formGroup()) {
|
|
880
|
+
const control = formGroup.controls[field.name];
|
|
556
881
|
if (control instanceof FormControl) {
|
|
557
882
|
return control;
|
|
558
883
|
}
|
|
@@ -578,6 +903,44 @@ class FormBuilderRenderer {
|
|
|
578
903
|
visibleChildren(field) {
|
|
579
904
|
return this.visibleFields(field.children ?? []);
|
|
580
905
|
}
|
|
906
|
+
isRepeaterField(field) {
|
|
907
|
+
return field.type === 'repeater';
|
|
908
|
+
}
|
|
909
|
+
repeaterArray(field, formGroup = this.formGroup()) {
|
|
910
|
+
const control = formGroup.controls[field.name];
|
|
911
|
+
return control instanceof FormArray
|
|
912
|
+
? control
|
|
913
|
+
: new FormArray([]);
|
|
914
|
+
}
|
|
915
|
+
repeaterGroups(field, formGroup = this.formGroup()) {
|
|
916
|
+
return this.repeaterArray(field, formGroup).controls;
|
|
917
|
+
}
|
|
918
|
+
repeaterEmptyText(field) {
|
|
919
|
+
const emptyText = field.settings?.['emptyText'];
|
|
920
|
+
return typeof emptyText === 'string' ? emptyText.trim() : '';
|
|
921
|
+
}
|
|
922
|
+
addRepeaterItem(field, formGroup = this.formGroup()) {
|
|
923
|
+
if (this.readonly()) {
|
|
924
|
+
return;
|
|
925
|
+
}
|
|
926
|
+
const array = this.repeaterArray(field, formGroup);
|
|
927
|
+
array.push(this.createRepeaterGroup(field));
|
|
928
|
+
array.updateValueAndValidity();
|
|
929
|
+
}
|
|
930
|
+
removeRepeaterItem(field, index, formGroup = this.formGroup()) {
|
|
931
|
+
if (this.readonly()) {
|
|
932
|
+
return;
|
|
933
|
+
}
|
|
934
|
+
if (!this.canRemoveRepeaterItem(field, formGroup)) {
|
|
935
|
+
return;
|
|
936
|
+
}
|
|
937
|
+
const array = this.repeaterArray(field, formGroup);
|
|
938
|
+
array.removeAt(index);
|
|
939
|
+
array.updateValueAndValidity();
|
|
940
|
+
}
|
|
941
|
+
canRemoveRepeaterItem(field, formGroup = this.formGroup()) {
|
|
942
|
+
return this.allowsNullValue(field) || this.repeaterArray(field, formGroup).length > 1;
|
|
943
|
+
}
|
|
581
944
|
submit() {
|
|
582
945
|
const form = this.formGroup();
|
|
583
946
|
if (form.invalid) {
|
|
@@ -590,12 +953,24 @@ class FormBuilderRenderer {
|
|
|
590
953
|
const controls = {};
|
|
591
954
|
const value = this.value();
|
|
592
955
|
const fields = [
|
|
593
|
-
...
|
|
594
|
-
...this.schema().sections.flatMap(section =>
|
|
956
|
+
...(this.schema().fields ?? []),
|
|
957
|
+
...this.schema().sections.flatMap(section => section.fields)
|
|
595
958
|
];
|
|
959
|
+
this.addFieldsToControls(controls, fields, value);
|
|
960
|
+
return new FormGroup(controls);
|
|
961
|
+
}
|
|
962
|
+
addFieldsToControls(controls, fields, value) {
|
|
596
963
|
for (const field of fields) {
|
|
597
964
|
const definition = this.definitions().find(item => item.type === field.type);
|
|
598
|
-
if (this.
|
|
965
|
+
if (this.isRepeaterField(field)) {
|
|
966
|
+
controls[field.name] = this.createRepeaterArray(field, value[field.name]);
|
|
967
|
+
continue;
|
|
968
|
+
}
|
|
969
|
+
if (this.isContainerField(field)) {
|
|
970
|
+
this.addFieldsToControls(controls, field.children ?? [], value);
|
|
971
|
+
continue;
|
|
972
|
+
}
|
|
973
|
+
if (definition?.kind === 'static' || field.kind === 'static') {
|
|
599
974
|
continue;
|
|
600
975
|
}
|
|
601
976
|
const validators = definition?.validators?.(field) ?? validatorsFromRules(field.validation, field);
|
|
@@ -605,6 +980,31 @@ class FormBuilderRenderer {
|
|
|
605
980
|
}, validators);
|
|
606
981
|
controls[field.name] = control;
|
|
607
982
|
}
|
|
983
|
+
}
|
|
984
|
+
createRepeaterArray(field, value) {
|
|
985
|
+
const allowNullValue = this.allowsNullValue(field);
|
|
986
|
+
const rows = Array.isArray(value) && (allowNullValue || value.length > 0)
|
|
987
|
+
? value
|
|
988
|
+
: allowNullValue
|
|
989
|
+
? []
|
|
990
|
+
: [{}];
|
|
991
|
+
const validators = allowNullValue ? [] : [repeaterRequiredValidator];
|
|
992
|
+
return new FormArray(rows.map(row => this.createRepeaterGroup(field, row)), validators);
|
|
993
|
+
}
|
|
994
|
+
createRepeaterGroup(field, value = {}) {
|
|
995
|
+
const controls = {};
|
|
996
|
+
const rowValue = isRecord(value) ? value : {};
|
|
997
|
+
for (const child of flattenFields(field.children ?? [])) {
|
|
998
|
+
const definition = this.definitions().find(item => item.type === child.type);
|
|
999
|
+
if (this.isContainerField(child) || definition?.kind === 'static' || child.kind === 'static') {
|
|
1000
|
+
continue;
|
|
1001
|
+
}
|
|
1002
|
+
const validators = definition?.validators?.(child) ?? validatorsFromRules(child.validation, child);
|
|
1003
|
+
controls[child.name] = new FormControl({
|
|
1004
|
+
value: rowValue[child.name] ?? this.fieldInitialValue(child),
|
|
1005
|
+
disabled: child.disabled || this.readonly()
|
|
1006
|
+
}, validators);
|
|
1007
|
+
}
|
|
608
1008
|
return new FormGroup(controls);
|
|
609
1009
|
}
|
|
610
1010
|
visibleFields(fields) {
|
|
@@ -630,6 +1030,9 @@ class FormBuilderRenderer {
|
|
|
630
1030
|
}
|
|
631
1031
|
return selectedValues[0] ?? null;
|
|
632
1032
|
}
|
|
1033
|
+
allowsNullValue(field) {
|
|
1034
|
+
return field.settings?.['allowNullValue'] === true;
|
|
1035
|
+
}
|
|
633
1036
|
resolveCanvasItems(schema) {
|
|
634
1037
|
const fieldsById = new Map((schema.fields ?? []).map(field => [field.id, field]));
|
|
635
1038
|
const sectionsById = new Map(schema.sections.map(section => [section.id, section]));
|
|
@@ -650,7 +1053,7 @@ class FormBuilderRenderer {
|
|
|
650
1053
|
return items;
|
|
651
1054
|
}
|
|
652
1055
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: FormBuilderRenderer, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
653
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.4", type: FormBuilderRenderer, isStandalone: true, selector: "ngs-form-renderer", inputs: { schema: { classPropertyName: "schema", publicName: "schema", isSignal: true, isRequired: true, transformFunction: null }, readonly: { classPropertyName: "readonly", publicName: "readonly", isSignal: true, isRequired: false, transformFunction: null }, showSubmit: { classPropertyName: "showSubmit", publicName: "showSubmit", isSignal: true, isRequired: false, transformFunction: null }, submitLabel: { classPropertyName: "submitLabel", publicName: "submitLabel", isSignal: true, isRequired: false, transformFunction: null }, uploadCallback: { classPropertyName: "uploadCallback", publicName: "uploadCallback", isSignal: true, isRequired: false, transformFunction: null }, value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { value: "valueChange", formSubmit: "formSubmit", formReady: "formReady" }, host: { classAttribute: "ngs-form-renderer" }, exportAs: ["ngsFormRenderer"], ngImport: i0, template: "<ng-template #renderFields let-fields>\n <div class=\"ngs-form-renderer-grid\">\n @for (field of fields; track field.id) {\n @if (isContainerField(field)) {\n <section class=\"ngs-form-renderer-grid-field\">\n <header>\n <h4>{{ field.label }}</h4>\n @if (field.hint) {\n <p>{{ field.hint }}</p>\n }\n </header>\n\n <ng-container\n [ngTemplateOutlet]=\"renderFields\"\n [ngTemplateOutletContext]=\"{ $implicit: visibleChildren(field) }\"/>\n </section>\n } @else {\n <ngs-form-builder-field-host\n [field]=\"field\"\n [control]=\"getControl(field)\"\n [definitions]=\"definitions()\"\n [uploadCallback]=\"uploadCallback()\"\n [readonly]=\"readonly()\"/>\n }\n }\n </div>\n</ng-template>\n\n<form [formGroup]=\"formGroup()\" (ngSubmit)=\"submit()\">\n <div class=\"flex flex-col gap-6\">\n @for (item of visibleCanvasItems(); track item.kind + ':' + item.id) {\n @if (item.field) {\n <ng-container\n [ngTemplateOutlet]=\"renderFields\"\n [ngTemplateOutletContext]=\"{ $implicit: [item.field] }\"/>\n } @else if (item.section; as section) {\n <section class=\"ngs-form-renderer-section\">\n @if (section.title || section.description) {\n <header>\n @if (section.title) {\n <h3>{{ section.title }}</h3>\n }\n @if (section.description) {\n <p>{{ section.description }}</p>\n }\n </header>\n }\n\n <ng-container\n [ngTemplateOutlet]=\"renderFields\"\n [ngTemplateOutletContext]=\"{ $implicit: section.fields }\"/>\n </section>\n }\n }\n\n @if (showSubmit() && !readonly()) {\n <div class=\"flex justify-end\">\n <button ngsButton=\"filled\" type=\"submit\">{{ submitLabel() }}</button>\n </div>\n }\n </div>\n</form>\n", styles: [":host{display:block}:host .ngs-form-renderer-section{display:flex;flex-direction:column;gap:calc(var(--spacing, .25rem) * 4)}:host .ngs-form-renderer-section header{display:flex;flex-direction:column;gap:calc(var(--spacing, .25rem) * 1)}:host .ngs-form-renderer-section header h3{color:var(--ngs-color-on-surface);font-size:var(--ngs-font-size-lg);font-weight:600}:host .ngs-form-renderer-section header p{color:var(--ngs-color-on-surface-variant);font-size:var(--ngs-font-size-sm)}:host .ngs-form-renderer-grid{display:grid;grid-template-columns:repeat(12,minmax(0,1fr));gap:calc(var(--spacing, .25rem) * 4)}:host .ngs-form-renderer-grid-field{display:flex;grid-column:1/-1;flex-direction:column;gap:calc(var(--spacing, .25rem) * 4);border:1px solid;border-color:var(--ngs-color-outline-variant);border-radius:var(--ngs-radius-lg);padding:calc(var(--spacing, .25rem) * 4)}:host .ngs-form-renderer-grid-field>header h4{color:var(--ngs-color-on-surface);font-size:var(--ngs-font-size-base);font-weight:600}:host .ngs-form-renderer-grid-field>header p{color:var(--ngs-color-on-surface-variant);font-size:var(--ngs-font-size-sm)}\n/*! tailwindcss v4.2.2 | MIT License | https://tailwindcss.com */\n"], dependencies: [{ kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],[formArray],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "component", type: Button, selector: " button[ngsButton], button[ngsIconButton], a[ngsButton], a[ngsIconButton] ", inputs: ["ngsButton", "ngsIconButton", "loading", "disabled", "disabledInteractive", "disableRipple", "reverse", "fullWidth", "hideTextOnMobile"], exportAs: ["ngsButton"] }, { kind: "component", type: FormBuilderFieldHost, selector: "ngs-form-builder-field-host", inputs: ["field", "control", "definitions", "readonly", "editableCanvas", "uploadCallback"], exportAs: ["ngsFormBuilderFieldHost"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
1056
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.4", type: FormBuilderRenderer, isStandalone: true, selector: "ngs-form-renderer", inputs: { schema: { classPropertyName: "schema", publicName: "schema", isSignal: true, isRequired: true, transformFunction: null }, readonly: { classPropertyName: "readonly", publicName: "readonly", isSignal: true, isRequired: false, transformFunction: null }, showSubmit: { classPropertyName: "showSubmit", publicName: "showSubmit", isSignal: true, isRequired: false, transformFunction: null }, submitLabel: { classPropertyName: "submitLabel", publicName: "submitLabel", isSignal: true, isRequired: false, transformFunction: null }, uploadCallback: { classPropertyName: "uploadCallback", publicName: "uploadCallback", isSignal: true, isRequired: false, transformFunction: null }, value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { value: "valueChange", formSubmit: "formSubmit", formReady: "formReady" }, host: { classAttribute: "ngs-form-renderer" }, exportAs: ["ngsFormRenderer"], ngImport: i0, template: "<ng-template #renderFields let-fields let-controlGroup=\"controlGroup\">\n <div class=\"ngs-form-renderer-grid\">\n @for (field of fields; track field.id) {\n @if (isRepeaterField(field)) {\n <section class=\"ngs-form-renderer-grid-field ngs-form-renderer-repeater\">\n <header>\n <div>\n <h4>{{ field.label }}</h4>\n @if (field.hint) {\n <p>{{ field.hint }}</p>\n }\n </div>\n\n @if (!readonly()) {\n <button ngsButton=\"outlined\" type=\"button\" (click)=\"addRepeaterItem(field, controlGroup)\">\n +\n </button>\n }\n </header>\n\n <div class=\"ngs-form-renderer-repeater-items\">\n @if (!repeaterGroups(field, controlGroup).length && repeaterEmptyText(field)) {\n <p class=\"ngs-form-renderer-repeater-empty\">{{ repeaterEmptyText(field) }}</p>\n }\n\n @for (group of repeaterGroups(field, controlGroup); track $index) {\n <div class=\"ngs-form-renderer-repeater-item\" [formGroup]=\"group\">\n <div class=\"ngs-form-renderer-repeater-item-header\">\n <span>{{ field.label }} {{ $index + 1 }}</span>\n @if (!readonly() && canRemoveRepeaterItem(field, controlGroup)) {\n <button ngsButton=\"text\" type=\"button\" (click)=\"removeRepeaterItem(field, $index, controlGroup)\">\n Remove\n </button>\n }\n </div>\n\n <ng-container\n [ngTemplateOutlet]=\"renderFields\"\n [ngTemplateOutletContext]=\"{ $implicit: visibleChildren(field), controlGroup: group }\"/>\n </div>\n }\n </div>\n </section>\n } @else if (isContainerField(field)) {\n <section class=\"ngs-form-renderer-grid-field\">\n <header>\n <h4>{{ field.label }}</h4>\n @if (field.hint) {\n <p>{{ field.hint }}</p>\n }\n </header>\n\n <ng-container\n [ngTemplateOutlet]=\"renderFields\"\n [ngTemplateOutletContext]=\"{ $implicit: visibleChildren(field), controlGroup: controlGroup }\"/>\n </section>\n } @else {\n <ngs-form-builder-field-host\n [field]=\"field\"\n [control]=\"getControl(field, controlGroup)\"\n [definitions]=\"definitions()\"\n [uploadCallback]=\"uploadCallback()\"\n [readonly]=\"readonly()\"/>\n }\n }\n </div>\n</ng-template>\n\n<form [formGroup]=\"formGroup()\" (ngSubmit)=\"submit()\">\n <div class=\"flex flex-col gap-6\">\n @for (item of visibleCanvasItems(); track item.kind + ':' + item.id) {\n @if (item.field) {\n <ng-container\n [ngTemplateOutlet]=\"renderFields\"\n [ngTemplateOutletContext]=\"{ $implicit: [item.field], controlGroup: formGroup() }\"/>\n } @else if (item.section; as section) {\n <section class=\"ngs-form-renderer-section\">\n @if (section.title || section.description) {\n <header>\n @if (section.title) {\n <h3>{{ section.title }}</h3>\n }\n @if (section.description) {\n <p>{{ section.description }}</p>\n }\n </header>\n }\n\n <ng-container\n [ngTemplateOutlet]=\"renderFields\"\n [ngTemplateOutletContext]=\"{ $implicit: section.fields, controlGroup: formGroup() }\"/>\n </section>\n }\n }\n\n @if (showSubmit() && !readonly()) {\n <div class=\"flex justify-end\">\n <button ngsButton=\"filled\" type=\"submit\">{{ submitLabel() }}</button>\n </div>\n }\n </div>\n</form>\n", styles: [":host{display:block}:host .ngs-form-renderer-section{display:flex;flex-direction:column;gap:calc(var(--spacing, .25rem) * 4)}:host .ngs-form-renderer-section header{display:flex;flex-direction:column;gap:calc(var(--spacing, .25rem) * 1)}:host .ngs-form-renderer-section header h3{color:var(--ngs-color-on-surface);font-size:var(--ngs-font-size-lg);font-weight:600}:host .ngs-form-renderer-section header p{color:var(--ngs-color-on-surface-variant);font-size:var(--ngs-font-size-sm)}:host .ngs-form-renderer-grid{display:grid;grid-template-columns:repeat(12,minmax(0,1fr));gap:calc(var(--spacing, .25rem) * 4)}:host .ngs-form-renderer-grid-field{display:flex;grid-column:1/-1;flex-direction:column;gap:calc(var(--spacing, .25rem) * 4);border:1px solid;border-color:var(--ngs-color-outline-variant);border-radius:var(--ngs-radius-lg);padding:calc(var(--spacing, .25rem) * 4)}:host .ngs-form-renderer-grid-field>header{display:flex;align-items:flex-start;justify-content:space-between;gap:calc(var(--spacing, .25rem) * 3)}:host .ngs-form-renderer-grid-field>header h4{color:var(--ngs-color-on-surface);font-size:var(--ngs-font-size-base);font-weight:600}:host .ngs-form-renderer-grid-field>header p{color:var(--ngs-color-on-surface-variant);font-size:var(--ngs-font-size-sm)}:host .ngs-form-renderer-repeater-items{display:flex;flex-direction:column;gap:calc(var(--spacing, .25rem) * 4)}:host .ngs-form-renderer-repeater-item{display:flex;flex-direction:column;gap:calc(var(--spacing, .25rem) * 4);border:1px dashed var(--ngs-color-outline-variant);border-radius:var(--ngs-radius-md);padding:calc(var(--spacing, .25rem) * 4)}:host .ngs-form-renderer-repeater-item-header{display:flex;align-items:center;justify-content:space-between;gap:calc(var(--spacing, .25rem) * 3)}:host .ngs-form-renderer-repeater-item-header span{color:var(--ngs-color-on-surface-variant);font-size:var(--ngs-font-size-sm);font-weight:500}:host .ngs-form-renderer-repeater-empty{color:var(--ngs-color-on-surface-variant);font-size:var(--ngs-font-size-sm);margin:0}\n/*! tailwindcss v4.2.2 | MIT License | https://tailwindcss.com */\n"], dependencies: [{ kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],[formArray],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "component", type: Button, selector: " button[ngsButton], button[ngsIconButton], a[ngsButton], a[ngsIconButton] ", inputs: ["ngsButton", "ngsIconButton", "loading", "disabled", "disabledInteractive", "disableRipple", "reverse", "fullWidth", "hideTextOnMobile"], exportAs: ["ngsButton"] }, { kind: "component", type: FormBuilderFieldHost, selector: "ngs-form-builder-field-host", inputs: ["field", "control", "definitions", "readonly", "editableCanvas", "uploadCallback"], exportAs: ["ngsFormBuilderFieldHost"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
654
1057
|
}
|
|
655
1058
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: FormBuilderRenderer, decorators: [{
|
|
656
1059
|
type: Component,
|
|
@@ -661,7 +1064,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.4", ngImpor
|
|
|
661
1064
|
FormBuilderFieldHost
|
|
662
1065
|
], changeDetection: ChangeDetectionStrategy.OnPush, host: {
|
|
663
1066
|
'class': 'ngs-form-renderer'
|
|
664
|
-
}, template: "<ng-template #renderFields let-fields>\n <div class=\"ngs-form-renderer-grid\">\n @for (field of fields; track field.id) {\n @if (isContainerField(field)) {\n <section class=\"ngs-form-renderer-grid-field\">\n <header>\n <h4>{{ field.label }}</h4>\n @if (field.hint) {\n <p>{{ field.hint }}</p>\n }\n </header>\n\n <ng-container\n [ngTemplateOutlet]=\"renderFields\"\n [ngTemplateOutletContext]=\"{ $implicit: visibleChildren(field) }\"/>\n </section>\n } @else {\n <ngs-form-builder-field-host\n [field]=\"field\"\n [control]=\"getControl(field)\"\n [definitions]=\"definitions()\"\n [uploadCallback]=\"uploadCallback()\"\n [readonly]=\"readonly()\"/>\n }\n }\n </div>\n</ng-template>\n\n<form [formGroup]=\"formGroup()\" (ngSubmit)=\"submit()\">\n <div class=\"flex flex-col gap-6\">\n @for (item of visibleCanvasItems(); track item.kind + ':' + item.id) {\n @if (item.field) {\n <ng-container\n [ngTemplateOutlet]=\"renderFields\"\n [ngTemplateOutletContext]=\"{ $implicit: [item.field] }\"/>\n } @else if (item.section; as section) {\n <section class=\"ngs-form-renderer-section\">\n @if (section.title || section.description) {\n <header>\n @if (section.title) {\n <h3>{{ section.title }}</h3>\n }\n @if (section.description) {\n <p>{{ section.description }}</p>\n }\n </header>\n }\n\n <ng-container\n [ngTemplateOutlet]=\"renderFields\"\n [ngTemplateOutletContext]=\"{ $implicit: section.fields }\"/>\n </section>\n }\n }\n\n @if (showSubmit() && !readonly()) {\n <div class=\"flex justify-end\">\n <button ngsButton=\"filled\" type=\"submit\">{{ submitLabel() }}</button>\n </div>\n }\n </div>\n</form>\n", styles: [":host{display:block}:host .ngs-form-renderer-section{display:flex;flex-direction:column;gap:calc(var(--spacing, .25rem) * 4)}:host .ngs-form-renderer-section header{display:flex;flex-direction:column;gap:calc(var(--spacing, .25rem) * 1)}:host .ngs-form-renderer-section header h3{color:var(--ngs-color-on-surface);font-size:var(--ngs-font-size-lg);font-weight:600}:host .ngs-form-renderer-section header p{color:var(--ngs-color-on-surface-variant);font-size:var(--ngs-font-size-sm)}:host .ngs-form-renderer-grid{display:grid;grid-template-columns:repeat(12,minmax(0,1fr));gap:calc(var(--spacing, .25rem) * 4)}:host .ngs-form-renderer-grid-field{display:flex;grid-column:1/-1;flex-direction:column;gap:calc(var(--spacing, .25rem) * 4);border:1px solid;border-color:var(--ngs-color-outline-variant);border-radius:var(--ngs-radius-lg);padding:calc(var(--spacing, .25rem) * 4)}:host .ngs-form-renderer-grid-field>header h4{color:var(--ngs-color-on-surface);font-size:var(--ngs-font-size-base);font-weight:600}:host .ngs-form-renderer-grid-field>header p{color:var(--ngs-color-on-surface-variant);font-size:var(--ngs-font-size-sm)}\n/*! tailwindcss v4.2.2 | MIT License | https://tailwindcss.com */\n"] }]
|
|
1067
|
+
}, template: "<ng-template #renderFields let-fields let-controlGroup=\"controlGroup\">\n <div class=\"ngs-form-renderer-grid\">\n @for (field of fields; track field.id) {\n @if (isRepeaterField(field)) {\n <section class=\"ngs-form-renderer-grid-field ngs-form-renderer-repeater\">\n <header>\n <div>\n <h4>{{ field.label }}</h4>\n @if (field.hint) {\n <p>{{ field.hint }}</p>\n }\n </div>\n\n @if (!readonly()) {\n <button ngsButton=\"outlined\" type=\"button\" (click)=\"addRepeaterItem(field, controlGroup)\">\n +\n </button>\n }\n </header>\n\n <div class=\"ngs-form-renderer-repeater-items\">\n @if (!repeaterGroups(field, controlGroup).length && repeaterEmptyText(field)) {\n <p class=\"ngs-form-renderer-repeater-empty\">{{ repeaterEmptyText(field) }}</p>\n }\n\n @for (group of repeaterGroups(field, controlGroup); track $index) {\n <div class=\"ngs-form-renderer-repeater-item\" [formGroup]=\"group\">\n <div class=\"ngs-form-renderer-repeater-item-header\">\n <span>{{ field.label }} {{ $index + 1 }}</span>\n @if (!readonly() && canRemoveRepeaterItem(field, controlGroup)) {\n <button ngsButton=\"text\" type=\"button\" (click)=\"removeRepeaterItem(field, $index, controlGroup)\">\n Remove\n </button>\n }\n </div>\n\n <ng-container\n [ngTemplateOutlet]=\"renderFields\"\n [ngTemplateOutletContext]=\"{ $implicit: visibleChildren(field), controlGroup: group }\"/>\n </div>\n }\n </div>\n </section>\n } @else if (isContainerField(field)) {\n <section class=\"ngs-form-renderer-grid-field\">\n <header>\n <h4>{{ field.label }}</h4>\n @if (field.hint) {\n <p>{{ field.hint }}</p>\n }\n </header>\n\n <ng-container\n [ngTemplateOutlet]=\"renderFields\"\n [ngTemplateOutletContext]=\"{ $implicit: visibleChildren(field), controlGroup: controlGroup }\"/>\n </section>\n } @else {\n <ngs-form-builder-field-host\n [field]=\"field\"\n [control]=\"getControl(field, controlGroup)\"\n [definitions]=\"definitions()\"\n [uploadCallback]=\"uploadCallback()\"\n [readonly]=\"readonly()\"/>\n }\n }\n </div>\n</ng-template>\n\n<form [formGroup]=\"formGroup()\" (ngSubmit)=\"submit()\">\n <div class=\"flex flex-col gap-6\">\n @for (item of visibleCanvasItems(); track item.kind + ':' + item.id) {\n @if (item.field) {\n <ng-container\n [ngTemplateOutlet]=\"renderFields\"\n [ngTemplateOutletContext]=\"{ $implicit: [item.field], controlGroup: formGroup() }\"/>\n } @else if (item.section; as section) {\n <section class=\"ngs-form-renderer-section\">\n @if (section.title || section.description) {\n <header>\n @if (section.title) {\n <h3>{{ section.title }}</h3>\n }\n @if (section.description) {\n <p>{{ section.description }}</p>\n }\n </header>\n }\n\n <ng-container\n [ngTemplateOutlet]=\"renderFields\"\n [ngTemplateOutletContext]=\"{ $implicit: section.fields, controlGroup: formGroup() }\"/>\n </section>\n }\n }\n\n @if (showSubmit() && !readonly()) {\n <div class=\"flex justify-end\">\n <button ngsButton=\"filled\" type=\"submit\">{{ submitLabel() }}</button>\n </div>\n }\n </div>\n</form>\n", styles: [":host{display:block}:host .ngs-form-renderer-section{display:flex;flex-direction:column;gap:calc(var(--spacing, .25rem) * 4)}:host .ngs-form-renderer-section header{display:flex;flex-direction:column;gap:calc(var(--spacing, .25rem) * 1)}:host .ngs-form-renderer-section header h3{color:var(--ngs-color-on-surface);font-size:var(--ngs-font-size-lg);font-weight:600}:host .ngs-form-renderer-section header p{color:var(--ngs-color-on-surface-variant);font-size:var(--ngs-font-size-sm)}:host .ngs-form-renderer-grid{display:grid;grid-template-columns:repeat(12,minmax(0,1fr));gap:calc(var(--spacing, .25rem) * 4)}:host .ngs-form-renderer-grid-field{display:flex;grid-column:1/-1;flex-direction:column;gap:calc(var(--spacing, .25rem) * 4);border:1px solid;border-color:var(--ngs-color-outline-variant);border-radius:var(--ngs-radius-lg);padding:calc(var(--spacing, .25rem) * 4)}:host .ngs-form-renderer-grid-field>header{display:flex;align-items:flex-start;justify-content:space-between;gap:calc(var(--spacing, .25rem) * 3)}:host .ngs-form-renderer-grid-field>header h4{color:var(--ngs-color-on-surface);font-size:var(--ngs-font-size-base);font-weight:600}:host .ngs-form-renderer-grid-field>header p{color:var(--ngs-color-on-surface-variant);font-size:var(--ngs-font-size-sm)}:host .ngs-form-renderer-repeater-items{display:flex;flex-direction:column;gap:calc(var(--spacing, .25rem) * 4)}:host .ngs-form-renderer-repeater-item{display:flex;flex-direction:column;gap:calc(var(--spacing, .25rem) * 4);border:1px dashed var(--ngs-color-outline-variant);border-radius:var(--ngs-radius-md);padding:calc(var(--spacing, .25rem) * 4)}:host .ngs-form-renderer-repeater-item-header{display:flex;align-items:center;justify-content:space-between;gap:calc(var(--spacing, .25rem) * 3)}:host .ngs-form-renderer-repeater-item-header span{color:var(--ngs-color-on-surface-variant);font-size:var(--ngs-font-size-sm);font-weight:500}:host .ngs-form-renderer-repeater-empty{color:var(--ngs-color-on-surface-variant);font-size:var(--ngs-font-size-sm);margin:0}\n/*! tailwindcss v4.2.2 | MIT License | https://tailwindcss.com */\n"] }]
|
|
665
1068
|
}], ctorParameters: () => [], propDecorators: { schema: [{ type: i0.Input, args: [{ isSignal: true, alias: "schema", required: true }] }], readonly: [{ type: i0.Input, args: [{ isSignal: true, alias: "readonly", required: false }] }], showSubmit: [{ type: i0.Input, args: [{ isSignal: true, alias: "showSubmit", required: false }] }], submitLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "submitLabel", required: false }] }], uploadCallback: [{ type: i0.Input, args: [{ isSignal: true, alias: "uploadCallback", required: false }] }], value: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: false }] }, { type: i0.Output, args: ["valueChange"] }], formSubmit: [{ type: i0.Output, args: ["formSubmit"] }], formReady: [{ type: i0.Output, args: ["formReady"] }] } });
|
|
666
1069
|
function normalizeFieldDefinition$1(definition) {
|
|
667
1070
|
return {
|
|
@@ -672,6 +1075,14 @@ function normalizeFieldDefinition$1(definition) {
|
|
|
672
1075
|
function flattenFields(fields) {
|
|
673
1076
|
return fields.flatMap(field => [field, ...flattenFields(field.children ?? [])]);
|
|
674
1077
|
}
|
|
1078
|
+
function isRecord(value) {
|
|
1079
|
+
return typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
1080
|
+
}
|
|
1081
|
+
function repeaterRequiredValidator(control) {
|
|
1082
|
+
return control instanceof FormArray && control.length === 0
|
|
1083
|
+
? { repeaterRequired: true }
|
|
1084
|
+
: null;
|
|
1085
|
+
}
|
|
675
1086
|
function normalizedLayout(schema) {
|
|
676
1087
|
const used = new Set();
|
|
677
1088
|
const layout = [];
|
|
@@ -704,250 +1115,6 @@ function normalizedLayout(schema) {
|
|
|
704
1115
|
return layout;
|
|
705
1116
|
}
|
|
706
1117
|
|
|
707
|
-
class BasicFormBuilderFieldSettings {
|
|
708
|
-
destroyRef = inject(DestroyRef);
|
|
709
|
-
field = input.required(...(ngDevMode ? [{ debugName: "field" }] : /* istanbul ignore next */ []));
|
|
710
|
-
update = input.required(...(ngDevMode ? [{ debugName: "update" }] : /* istanbul ignore next */ []));
|
|
711
|
-
optionsControl = new FormControl('', {
|
|
712
|
-
nonNullable: true,
|
|
713
|
-
validators: optionsTextValidator
|
|
714
|
-
});
|
|
715
|
-
fieldWidthOptions = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12];
|
|
716
|
-
radioOrientationOptions = ['vertical', 'horizontal'];
|
|
717
|
-
spacerHeightOptions = [8, 16, 24, 32, 48, 64];
|
|
718
|
-
hasCheckedState = computed(() => ['checkbox', 'toggle'].includes(this.field().type), ...(ngDevMode ? [{ debugName: "hasCheckedState" }] : /* istanbul ignore next */ []));
|
|
719
|
-
hasPlaceholder = computed(() => !['checkbox', 'toggle', 'radio', 'spacer'].includes(this.field().type), ...(ngDevMode ? [{ debugName: "hasPlaceholder" }] : /* istanbul ignore next */ []));
|
|
720
|
-
hasOptions = computed(() => ['select', 'radio', 'checkbox-list'].includes(this.field().type), ...(ngDevMode ? [{ debugName: "hasOptions" }] : /* istanbul ignore next */ []));
|
|
721
|
-
hasClearable = computed(() => ['select', 'country-select'].includes(this.field().type), ...(ngDevMode ? [{ debugName: "hasClearable" }] : /* istanbul ignore next */ []));
|
|
722
|
-
hasMultiple = computed(() => ['select', 'upload'].includes(this.field().type), ...(ngDevMode ? [{ debugName: "hasMultiple" }] : /* istanbul ignore next */ []));
|
|
723
|
-
isSelect = computed(() => this.field().type === 'select', ...(ngDevMode ? [{ debugName: "isSelect" }] : /* istanbul ignore next */ []));
|
|
724
|
-
isUpload = computed(() => this.field().type === 'upload', ...(ngDevMode ? [{ debugName: "isUpload" }] : /* istanbul ignore next */ []));
|
|
725
|
-
isRadio = computed(() => this.field().type === 'radio', ...(ngDevMode ? [{ debugName: "isRadio" }] : /* istanbul ignore next */ []));
|
|
726
|
-
isSpacer = computed(() => this.field().type === 'spacer', ...(ngDevMode ? [{ debugName: "isSpacer" }] : /* istanbul ignore next */ []));
|
|
727
|
-
hasBehaviorToggles = computed(() => !this.isSpacer(), ...(ngDevMode ? [{ debugName: "hasBehaviorToggles" }] : /* istanbul ignore next */ []));
|
|
728
|
-
radioOrientation = computed(() => this.field().settings?.['orientation'] === 'horizontal' ? 'horizontal' : 'vertical', ...(ngDevMode ? [{ debugName: "radioOrientation" }] : /* istanbul ignore next */ []));
|
|
729
|
-
optionsText = computed(() => (this.field().options ?? [])
|
|
730
|
-
.map(option => `${option.label}:${option.value}${this.isOptionSelected(option) ? ':selected' : ''}`)
|
|
731
|
-
.join('\n'), ...(ngDevMode ? [{ debugName: "optionsText" }] : /* istanbul ignore next */ []));
|
|
732
|
-
constructor() {
|
|
733
|
-
effect(() => {
|
|
734
|
-
const nextValue = this.optionsText();
|
|
735
|
-
if (this.optionsControl.value !== nextValue) {
|
|
736
|
-
this.optionsControl.setValue(nextValue, { emitEvent: false });
|
|
737
|
-
}
|
|
738
|
-
});
|
|
739
|
-
this.optionsControl.valueChanges
|
|
740
|
-
.pipe(takeUntilDestroyed(this.destroyRef))
|
|
741
|
-
.subscribe(value => {
|
|
742
|
-
if (this.optionsControl.invalid) {
|
|
743
|
-
return;
|
|
744
|
-
}
|
|
745
|
-
this.patchOptions(value);
|
|
746
|
-
});
|
|
747
|
-
}
|
|
748
|
-
patch(changes) {
|
|
749
|
-
this.update()(changes);
|
|
750
|
-
}
|
|
751
|
-
patchSettings(changes) {
|
|
752
|
-
this.patch({
|
|
753
|
-
settings: {
|
|
754
|
-
...this.field().settings,
|
|
755
|
-
...changes
|
|
756
|
-
}
|
|
757
|
-
});
|
|
758
|
-
}
|
|
759
|
-
patchMultiple(multiple) {
|
|
760
|
-
if (this.field().type === 'select') {
|
|
761
|
-
this.patchSelectMultiple(multiple);
|
|
762
|
-
return;
|
|
763
|
-
}
|
|
764
|
-
this.patch({
|
|
765
|
-
multiple,
|
|
766
|
-
defaultValue: multiple ? [] : null
|
|
767
|
-
});
|
|
768
|
-
}
|
|
769
|
-
patchSelectMultiple(multiple) {
|
|
770
|
-
const field = this.field();
|
|
771
|
-
const options = multiple ? field.options ?? [] : this.normalizeSelectedOptions(field.options ?? [], false);
|
|
772
|
-
const selectedValues = this.selectedOptionValues(options);
|
|
773
|
-
let defaultValue = null;
|
|
774
|
-
if (multiple) {
|
|
775
|
-
defaultValue = Array.isArray(field.defaultValue)
|
|
776
|
-
? field.defaultValue
|
|
777
|
-
: field.defaultValue == null
|
|
778
|
-
? selectedValues
|
|
779
|
-
: [field.defaultValue];
|
|
780
|
-
}
|
|
781
|
-
else {
|
|
782
|
-
defaultValue = Array.isArray(field.defaultValue)
|
|
783
|
-
? field.defaultValue[0] ?? selectedValues[0] ?? null
|
|
784
|
-
: field.defaultValue ?? selectedValues[0] ?? null;
|
|
785
|
-
}
|
|
786
|
-
this.patch({ multiple, options, defaultValue });
|
|
787
|
-
}
|
|
788
|
-
patchRadioOrientation(orientation) {
|
|
789
|
-
this.patch({
|
|
790
|
-
settings: {
|
|
791
|
-
...this.field().settings,
|
|
792
|
-
orientation
|
|
793
|
-
}
|
|
794
|
-
});
|
|
795
|
-
}
|
|
796
|
-
patchOptions(value) {
|
|
797
|
-
const options = this.normalizeSelectedOptions(value
|
|
798
|
-
.split('\n')
|
|
799
|
-
.map(line => line.trim())
|
|
800
|
-
.filter(Boolean)
|
|
801
|
-
.map((line, index) => {
|
|
802
|
-
const parsed = parseOptionLine(line);
|
|
803
|
-
return {
|
|
804
|
-
label: parsed?.label || `Option ${index + 1}`,
|
|
805
|
-
value: parsed?.value || `option_${index + 1}`,
|
|
806
|
-
selected: parsed?.selected || undefined
|
|
807
|
-
};
|
|
808
|
-
}));
|
|
809
|
-
this.patch({
|
|
810
|
-
options,
|
|
811
|
-
defaultValue: this.resolveSelectedDefaultValue(options)
|
|
812
|
-
});
|
|
813
|
-
}
|
|
814
|
-
normalizeSelectedOptions(options, multiple = this.field().multiple) {
|
|
815
|
-
if (this.field().type === 'checkbox-list' || multiple) {
|
|
816
|
-
return options;
|
|
817
|
-
}
|
|
818
|
-
let selectedSeen = false;
|
|
819
|
-
return options.map(option => {
|
|
820
|
-
if (!option.selected) {
|
|
821
|
-
return option;
|
|
822
|
-
}
|
|
823
|
-
if (selectedSeen) {
|
|
824
|
-
return {
|
|
825
|
-
...option,
|
|
826
|
-
selected: undefined
|
|
827
|
-
};
|
|
828
|
-
}
|
|
829
|
-
selectedSeen = true;
|
|
830
|
-
return option;
|
|
831
|
-
});
|
|
832
|
-
}
|
|
833
|
-
resolveSelectedDefaultValue(options) {
|
|
834
|
-
const selectedValues = this.selectedOptionValues(options);
|
|
835
|
-
if (this.field().type === 'checkbox-list' || this.field().multiple) {
|
|
836
|
-
return selectedValues;
|
|
837
|
-
}
|
|
838
|
-
return selectedValues[0] ?? null;
|
|
839
|
-
}
|
|
840
|
-
selectedOptionValues(options) {
|
|
841
|
-
return options
|
|
842
|
-
.filter(option => option.selected)
|
|
843
|
-
.map(option => option.value);
|
|
844
|
-
}
|
|
845
|
-
isOptionSelected(option) {
|
|
846
|
-
if (option.selected) {
|
|
847
|
-
return true;
|
|
848
|
-
}
|
|
849
|
-
const defaultValue = this.field().defaultValue;
|
|
850
|
-
return Array.isArray(defaultValue)
|
|
851
|
-
? defaultValue.includes(option.value)
|
|
852
|
-
: defaultValue === option.value;
|
|
853
|
-
}
|
|
854
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: BasicFormBuilderFieldSettings, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
855
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.4", type: BasicFormBuilderFieldSettings, isStandalone: true, selector: "ngs-basic-form-builder-field-settings", inputs: { field: { classPropertyName: "field", publicName: "field", isSignal: true, isRequired: true, transformFunction: null }, update: { classPropertyName: "update", publicName: "update", isSignal: true, isRequired: true, transformFunction: null } }, host: { classAttribute: "ngs-basic-form-builder-field-settings" }, exportAs: ["ngsBasicFormBuilderFieldSettings"], ngImport: i0, template: "<div class=\"flex flex-col gap-4\">\n <ngs-form-field>\n <ngs-label>Label</ngs-label>\n <input ngsInput\n [ngModel]=\"field().label\"\n [ngModelOptions]=\"{standalone: true}\"\n (ngModelChange)=\"patch({ label: $event })\">\n </ngs-form-field>\n\n @if (!isSpacer()) {\n <ngs-form-field>\n <ngs-label>Field ID</ngs-label>\n <input ngsInput\n [ngModel]=\"field().name\"\n [ngModelOptions]=\"{standalone: true}\"\n (ngModelChange)=\"patch({ name: $event })\">\n </ngs-form-field>\n }\n\n @if (hasPlaceholder()) {\n <ngs-form-field>\n <ngs-label>Placeholder</ngs-label>\n <input ngsInput\n [ngModel]=\"field().placeholder || ''\"\n [ngModelOptions]=\"{standalone: true}\"\n (ngModelChange)=\"patch({ placeholder: $event })\">\n </ngs-form-field>\n }\n\n @if (!isSpacer()) {\n <ngs-form-field>\n <ngs-label>Hint</ngs-label>\n <input ngsInput\n [ngModel]=\"field().hint || ''\"\n [ngModelOptions]=\"{standalone: true}\"\n (ngModelChange)=\"patch({ hint: $event })\">\n </ngs-form-field>\n }\n\n <ngs-form-field>\n <ngs-label>Width</ngs-label>\n <ngs-select [ngModel]=\"field().width ?? 12\"\n [ngModelOptions]=\"{standalone: true}\"\n (ngModelChange)=\"patch({ width: $event })\">\n @for (width of fieldWidthOptions; track width) {\n <ngs-option [value]=\"width\">{{ width }}/12</ngs-option>\n }\n </ngs-select>\n </ngs-form-field>\n\n @if (hasBehaviorToggles()) {\n <ngs-slide-toggle-group class=\"mb-4\">\n <ngs-slide-toggle [ngModel]=\"field().required ?? false\"\n [ngModelOptions]=\"{standalone: true}\"\n (ngModelChange)=\"patch({ required: $event })\">\n Required field\n </ngs-slide-toggle>\n\n <ngs-slide-toggle [ngModel]=\"field().readonly ?? false\"\n [ngModelOptions]=\"{standalone: true}\"\n (ngModelChange)=\"patch({ readonly: $event })\">\n Readonly\n </ngs-slide-toggle>\n\n <ngs-slide-toggle [ngModel]=\"field().disabled ?? false\"\n [ngModelOptions]=\"{standalone: true}\"\n (ngModelChange)=\"patch({ disabled: $event })\">\n Disabled\n </ngs-slide-toggle>\n\n @if (hasCheckedState()) {\n <ngs-slide-toggle [ngModel]=\"field().defaultValue === true\"\n [ngModelOptions]=\"{standalone: true}\"\n (ngModelChange)=\"patch({ defaultValue: $event })\">\n Checked\n </ngs-slide-toggle>\n }\n\n @if (hasMultiple()) {\n <ngs-slide-toggle [ngModel]=\"field().multiple ?? false\"\n [ngModelOptions]=\"{standalone: true}\"\n (ngModelChange)=\"patchMultiple($event)\">\n Multiple\n </ngs-slide-toggle>\n }\n\n @if (hasClearable()) {\n <ngs-slide-toggle [ngModel]=\"field().clearable ?? false\"\n [ngModelOptions]=\"{standalone: true}\"\n (ngModelChange)=\"patch({ clearable: $event })\">\n Clearable\n </ngs-slide-toggle>\n }\n </ngs-slide-toggle-group>\n }\n\n @if (isUpload()) {\n <ngs-form-field>\n <ngs-label>Accepted file types</ngs-label>\n <input ngsInput\n [ngModel]=\"field().settings?.['accept'] || ''\"\n [ngModelOptions]=\"{standalone: true}\"\n (ngModelChange)=\"patchSettings({ accept: $event })\">\n <ngs-hint>Use MIME types separated by commas, for example image/*,application/pdf.</ngs-hint>\n </ngs-form-field>\n }\n\n @if (isSpacer()) {\n <ngs-form-field>\n <ngs-label>Height</ngs-label>\n <ngs-select [ngModel]=\"field().settings?.['height'] ?? 24\"\n [ngModelOptions]=\"{standalone: true}\"\n (ngModelChange)=\"patchSettings({ height: $event })\">\n @for (height of spacerHeightOptions; track height) {\n <ngs-option [value]=\"height\">{{ height }}px</ngs-option>\n }\n </ngs-select>\n </ngs-form-field>\n }\n\n @if (isRadio()) {\n <ngs-form-field>\n <ngs-label>Orientation</ngs-label>\n <ngs-select [ngModel]=\"radioOrientation()\"\n [ngModelOptions]=\"{standalone: true}\"\n (ngModelChange)=\"patchRadioOrientation($event)\">\n @for (orientation of radioOrientationOptions; track orientation) {\n <ngs-option [value]=\"orientation\">{{ orientation }}</ngs-option>\n }\n </ngs-select>\n </ngs-form-field>\n }\n\n @if (hasOptions()) {\n <ngs-form-field>\n <ngs-label>Options</ngs-label>\n <textarea ngsInput\n rows=\"4\"\n [formControl]=\"optionsControl\"></textarea>\n @if (optionsControl.hasError('formBuilderOptionsFormat')) {\n <ngs-error>Use Label:value or Label:value:selected.</ngs-error>\n }\n <ngs-hint>One option per line. Use Label:value or Label:value:selected.</ngs-hint>\n </ngs-form-field>\n }\n</div>\n", styles: [":host{display:block}:host .settings-section{display:flex;flex-direction:column;gap:calc(var(--spacing, .25rem) * 3);border-top:1px solid var(--ngs-color-outline-variant);padding-top:calc(var(--spacing, .25rem) * 4)}:host .settings-section h4{color:var(--ngs-color-on-surface);font-size:var(--ngs-font-size-sm);font-weight:600}\n/*! tailwindcss v4.2.2 | MIT License | https://tailwindcss.com */\n"], dependencies: [{ kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1.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: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }, { kind: "component", type: Error, selector: "ngs-error", exportAs: ["ngsError"] }, { kind: "component", type: FormField, selector: "ngs-form-field", inputs: ["subscriptHiddenIfEmpty", "sameHeightAsButton"], exportAs: ["ngsFormField"] }, { kind: "component", type: Hint, selector: "ngs-hint", inputs: ["align"], exportAs: ["ngsHint"] }, { kind: "component", type: Label, selector: "ngs-label" }, { kind: "directive", type: Input, selector: "input[ngsInput], textarea[ngsInput]", inputs: ["id", "placeholder", "required", "disabled", "readonly", "errorStateMatcher"], exportAs: ["ngsInput"] }, { kind: "component", type: Select, selector: "ngs-select", inputs: ["id", "placeholder", "disabled", "required", "multiple", "hideCheckIcon", "clearable", "aria-label", "tabIndex", "aria-describedby", "value"], outputs: ["selectionChange", "opened", "closed", "valueChange"], exportAs: ["ngsSelect"] }, { kind: "component", type: Option, selector: "ngs-option", inputs: ["value", "data", "disabled", "selected"], outputs: ["onSelectionChange"], exportAs: ["ngsOption"] }, { kind: "component", type: SlideToggle, selector: "ngs-slide-toggle", inputs: ["id", "name", "labelPosition", "aria-label", "aria-labelledby", "aria-describedby", "required", "disabled", "disableRipple", "tabIndex", "hideIcon", "color", "checked"], outputs: ["disabledChange", "checkedChange", "change", "toggleChange"], exportAs: ["ngsSlideToggle"] }, { kind: "component", type: SlideToggleGroup, selector: "ngs-slide-toggle-group", exportAs: ["ngsSlideToggleGroup"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
856
|
-
}
|
|
857
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: BasicFormBuilderFieldSettings, decorators: [{
|
|
858
|
-
type: Component,
|
|
859
|
-
args: [{ selector: 'ngs-basic-form-builder-field-settings', exportAs: 'ngsBasicFormBuilderFieldSettings', imports: [
|
|
860
|
-
FormsModule,
|
|
861
|
-
ReactiveFormsModule,
|
|
862
|
-
Error,
|
|
863
|
-
FormField,
|
|
864
|
-
Hint,
|
|
865
|
-
Label,
|
|
866
|
-
Input,
|
|
867
|
-
Select,
|
|
868
|
-
Option,
|
|
869
|
-
SlideToggle,
|
|
870
|
-
SlideToggleGroup
|
|
871
|
-
], changeDetection: ChangeDetectionStrategy.OnPush, host: {
|
|
872
|
-
'class': 'ngs-basic-form-builder-field-settings'
|
|
873
|
-
}, template: "<div class=\"flex flex-col gap-4\">\n <ngs-form-field>\n <ngs-label>Label</ngs-label>\n <input ngsInput\n [ngModel]=\"field().label\"\n [ngModelOptions]=\"{standalone: true}\"\n (ngModelChange)=\"patch({ label: $event })\">\n </ngs-form-field>\n\n @if (!isSpacer()) {\n <ngs-form-field>\n <ngs-label>Field ID</ngs-label>\n <input ngsInput\n [ngModel]=\"field().name\"\n [ngModelOptions]=\"{standalone: true}\"\n (ngModelChange)=\"patch({ name: $event })\">\n </ngs-form-field>\n }\n\n @if (hasPlaceholder()) {\n <ngs-form-field>\n <ngs-label>Placeholder</ngs-label>\n <input ngsInput\n [ngModel]=\"field().placeholder || ''\"\n [ngModelOptions]=\"{standalone: true}\"\n (ngModelChange)=\"patch({ placeholder: $event })\">\n </ngs-form-field>\n }\n\n @if (!isSpacer()) {\n <ngs-form-field>\n <ngs-label>Hint</ngs-label>\n <input ngsInput\n [ngModel]=\"field().hint || ''\"\n [ngModelOptions]=\"{standalone: true}\"\n (ngModelChange)=\"patch({ hint: $event })\">\n </ngs-form-field>\n }\n\n <ngs-form-field>\n <ngs-label>Width</ngs-label>\n <ngs-select [ngModel]=\"field().width ?? 12\"\n [ngModelOptions]=\"{standalone: true}\"\n (ngModelChange)=\"patch({ width: $event })\">\n @for (width of fieldWidthOptions; track width) {\n <ngs-option [value]=\"width\">{{ width }}/12</ngs-option>\n }\n </ngs-select>\n </ngs-form-field>\n\n @if (hasBehaviorToggles()) {\n <ngs-slide-toggle-group class=\"mb-4\">\n <ngs-slide-toggle [ngModel]=\"field().required ?? false\"\n [ngModelOptions]=\"{standalone: true}\"\n (ngModelChange)=\"patch({ required: $event })\">\n Required field\n </ngs-slide-toggle>\n\n <ngs-slide-toggle [ngModel]=\"field().readonly ?? false\"\n [ngModelOptions]=\"{standalone: true}\"\n (ngModelChange)=\"patch({ readonly: $event })\">\n Readonly\n </ngs-slide-toggle>\n\n <ngs-slide-toggle [ngModel]=\"field().disabled ?? false\"\n [ngModelOptions]=\"{standalone: true}\"\n (ngModelChange)=\"patch({ disabled: $event })\">\n Disabled\n </ngs-slide-toggle>\n\n @if (hasCheckedState()) {\n <ngs-slide-toggle [ngModel]=\"field().defaultValue === true\"\n [ngModelOptions]=\"{standalone: true}\"\n (ngModelChange)=\"patch({ defaultValue: $event })\">\n Checked\n </ngs-slide-toggle>\n }\n\n @if (hasMultiple()) {\n <ngs-slide-toggle [ngModel]=\"field().multiple ?? false\"\n [ngModelOptions]=\"{standalone: true}\"\n (ngModelChange)=\"patchMultiple($event)\">\n Multiple\n </ngs-slide-toggle>\n }\n\n @if (hasClearable()) {\n <ngs-slide-toggle [ngModel]=\"field().clearable ?? false\"\n [ngModelOptions]=\"{standalone: true}\"\n (ngModelChange)=\"patch({ clearable: $event })\">\n Clearable\n </ngs-slide-toggle>\n }\n </ngs-slide-toggle-group>\n }\n\n @if (isUpload()) {\n <ngs-form-field>\n <ngs-label>Accepted file types</ngs-label>\n <input ngsInput\n [ngModel]=\"field().settings?.['accept'] || ''\"\n [ngModelOptions]=\"{standalone: true}\"\n (ngModelChange)=\"patchSettings({ accept: $event })\">\n <ngs-hint>Use MIME types separated by commas, for example image/*,application/pdf.</ngs-hint>\n </ngs-form-field>\n }\n\n @if (isSpacer()) {\n <ngs-form-field>\n <ngs-label>Height</ngs-label>\n <ngs-select [ngModel]=\"field().settings?.['height'] ?? 24\"\n [ngModelOptions]=\"{standalone: true}\"\n (ngModelChange)=\"patchSettings({ height: $event })\">\n @for (height of spacerHeightOptions; track height) {\n <ngs-option [value]=\"height\">{{ height }}px</ngs-option>\n }\n </ngs-select>\n </ngs-form-field>\n }\n\n @if (isRadio()) {\n <ngs-form-field>\n <ngs-label>Orientation</ngs-label>\n <ngs-select [ngModel]=\"radioOrientation()\"\n [ngModelOptions]=\"{standalone: true}\"\n (ngModelChange)=\"patchRadioOrientation($event)\">\n @for (orientation of radioOrientationOptions; track orientation) {\n <ngs-option [value]=\"orientation\">{{ orientation }}</ngs-option>\n }\n </ngs-select>\n </ngs-form-field>\n }\n\n @if (hasOptions()) {\n <ngs-form-field>\n <ngs-label>Options</ngs-label>\n <textarea ngsInput\n rows=\"4\"\n [formControl]=\"optionsControl\"></textarea>\n @if (optionsControl.hasError('formBuilderOptionsFormat')) {\n <ngs-error>Use Label:value or Label:value:selected.</ngs-error>\n }\n <ngs-hint>One option per line. Use Label:value or Label:value:selected.</ngs-hint>\n </ngs-form-field>\n }\n</div>\n", styles: [":host{display:block}:host .settings-section{display:flex;flex-direction:column;gap:calc(var(--spacing, .25rem) * 3);border-top:1px solid var(--ngs-color-outline-variant);padding-top:calc(var(--spacing, .25rem) * 4)}:host .settings-section h4{color:var(--ngs-color-on-surface);font-size:var(--ngs-font-size-sm);font-weight:600}\n/*! tailwindcss v4.2.2 | MIT License | https://tailwindcss.com */\n"] }]
|
|
874
|
-
}], ctorParameters: () => [], propDecorators: { field: [{ type: i0.Input, args: [{ isSignal: true, alias: "field", required: true }] }], update: [{ type: i0.Input, args: [{ isSignal: true, alias: "update", required: true }] }] } });
|
|
875
|
-
function optionsTextValidator(control) {
|
|
876
|
-
const hasInvalidLine = (control.value ?? '')
|
|
877
|
-
.split('\n')
|
|
878
|
-
.map(line => line.trim())
|
|
879
|
-
.filter(Boolean)
|
|
880
|
-
.some(line => !parseOptionLine(line));
|
|
881
|
-
return hasInvalidLine ? { formBuilderOptionsFormat: true } : null;
|
|
882
|
-
}
|
|
883
|
-
function parseOptionLine(line) {
|
|
884
|
-
const parts = line.split(':').map(part => part.trim());
|
|
885
|
-
if (parts.length < 2 || parts.length > 3) {
|
|
886
|
-
return null;
|
|
887
|
-
}
|
|
888
|
-
const [label, value, selected] = parts;
|
|
889
|
-
if (!label || !value) {
|
|
890
|
-
return null;
|
|
891
|
-
}
|
|
892
|
-
if (selected !== undefined && selected !== 'selected') {
|
|
893
|
-
return null;
|
|
894
|
-
}
|
|
895
|
-
return {
|
|
896
|
-
label,
|
|
897
|
-
value,
|
|
898
|
-
selected: selected === 'selected'
|
|
899
|
-
};
|
|
900
|
-
}
|
|
901
|
-
|
|
902
|
-
class BasicFormBuilderLayoutSettings {
|
|
903
|
-
field = input.required(...(ngDevMode ? [{ debugName: "field" }] : /* istanbul ignore next */ []));
|
|
904
|
-
update = input.required(...(ngDevMode ? [{ debugName: "update" }] : /* istanbul ignore next */ []));
|
|
905
|
-
fieldWidthOptions = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12];
|
|
906
|
-
showHintSetting = computed(() => this.field().type !== 'group', ...(ngDevMode ? [{ debugName: "showHintSetting" }] : /* istanbul ignore next */ []));
|
|
907
|
-
patch(changes) {
|
|
908
|
-
this.update()(changes);
|
|
909
|
-
}
|
|
910
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: BasicFormBuilderLayoutSettings, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
911
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.4", type: BasicFormBuilderLayoutSettings, isStandalone: true, selector: "ngs-basic-form-builder-layout-settings", inputs: { field: { classPropertyName: "field", publicName: "field", isSignal: true, isRequired: true, transformFunction: null }, update: { classPropertyName: "update", publicName: "update", isSignal: true, isRequired: true, transformFunction: null } }, host: { classAttribute: "ngs-basic-form-builder-layout-settings" }, exportAs: ["ngsBasicFormBuilderLayoutSettings"], ngImport: i0, template: "<div class=\"flex flex-col gap-4\">\n <ngs-form-field>\n <ngs-label>Label</ngs-label>\n <input ngsInput\n [ngModel]=\"field().label\"\n [ngModelOptions]=\"{standalone: true}\"\n (ngModelChange)=\"patch({ label: $event })\">\n </ngs-form-field>\n\n @if (showHintSetting()) {\n <ngs-form-field>\n <ngs-label>Hint</ngs-label>\n <input ngsInput\n [ngModel]=\"field().hint || ''\"\n [ngModelOptions]=\"{standalone: true}\"\n (ngModelChange)=\"patch({ hint: $event })\">\n <ngs-hint>Shown below the layout title in preview and renderer.</ngs-hint>\n </ngs-form-field>\n }\n\n <ngs-form-field>\n <ngs-label>Width</ngs-label>\n <ngs-select [ngModel]=\"field().width ?? 12\"\n [ngModelOptions]=\"{standalone: true}\"\n (ngModelChange)=\"patch({ width: $event })\">\n @for (width of fieldWidthOptions; track width) {\n <ngs-option [value]=\"width\">{{ width }}/12</ngs-option>\n }\n </ngs-select>\n </ngs-form-field>\n</div>\n", styles: [":host{display:block}\n/*! tailwindcss v4.2.2 | MIT License | https://tailwindcss.com */\n"], dependencies: [{ kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1.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: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "component", type: FormField, selector: "ngs-form-field", inputs: ["subscriptHiddenIfEmpty", "sameHeightAsButton"], exportAs: ["ngsFormField"] }, { kind: "component", type: Hint, selector: "ngs-hint", inputs: ["align"], exportAs: ["ngsHint"] }, { kind: "component", type: Label, selector: "ngs-label" }, { kind: "directive", type: Input, selector: "input[ngsInput], textarea[ngsInput]", inputs: ["id", "placeholder", "required", "disabled", "readonly", "errorStateMatcher"], exportAs: ["ngsInput"] }, { kind: "component", type: Option, selector: "ngs-option", inputs: ["value", "data", "disabled", "selected"], outputs: ["onSelectionChange"], exportAs: ["ngsOption"] }, { kind: "component", type: Select, selector: "ngs-select", inputs: ["id", "placeholder", "disabled", "required", "multiple", "hideCheckIcon", "clearable", "aria-label", "tabIndex", "aria-describedby", "value"], outputs: ["selectionChange", "opened", "closed", "valueChange"], exportAs: ["ngsSelect"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
912
|
-
}
|
|
913
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: BasicFormBuilderLayoutSettings, decorators: [{
|
|
914
|
-
type: Component,
|
|
915
|
-
args: [{ selector: 'ngs-basic-form-builder-layout-settings', exportAs: 'ngsBasicFormBuilderLayoutSettings', imports: [
|
|
916
|
-
FormsModule,
|
|
917
|
-
FormField,
|
|
918
|
-
Hint,
|
|
919
|
-
Label,
|
|
920
|
-
Input,
|
|
921
|
-
Option,
|
|
922
|
-
Select
|
|
923
|
-
], changeDetection: ChangeDetectionStrategy.OnPush, host: {
|
|
924
|
-
'class': 'ngs-basic-form-builder-layout-settings'
|
|
925
|
-
}, template: "<div class=\"flex flex-col gap-4\">\n <ngs-form-field>\n <ngs-label>Label</ngs-label>\n <input ngsInput\n [ngModel]=\"field().label\"\n [ngModelOptions]=\"{standalone: true}\"\n (ngModelChange)=\"patch({ label: $event })\">\n </ngs-form-field>\n\n @if (showHintSetting()) {\n <ngs-form-field>\n <ngs-label>Hint</ngs-label>\n <input ngsInput\n [ngModel]=\"field().hint || ''\"\n [ngModelOptions]=\"{standalone: true}\"\n (ngModelChange)=\"patch({ hint: $event })\">\n <ngs-hint>Shown below the layout title in preview and renderer.</ngs-hint>\n </ngs-form-field>\n }\n\n <ngs-form-field>\n <ngs-label>Width</ngs-label>\n <ngs-select [ngModel]=\"field().width ?? 12\"\n [ngModelOptions]=\"{standalone: true}\"\n (ngModelChange)=\"patch({ width: $event })\">\n @for (width of fieldWidthOptions; track width) {\n <ngs-option [value]=\"width\">{{ width }}/12</ngs-option>\n }\n </ngs-select>\n </ngs-form-field>\n</div>\n", styles: [":host{display:block}\n/*! tailwindcss v4.2.2 | MIT License | https://tailwindcss.com */\n"] }]
|
|
926
|
-
}], propDecorators: { field: [{ type: i0.Input, args: [{ isSignal: true, alias: "field", required: true }] }], update: [{ type: i0.Input, args: [{ isSignal: true, alias: "update", required: true }] }] } });
|
|
927
|
-
|
|
928
|
-
class BasicFormBuilderSectionSettings {
|
|
929
|
-
section = input.required(...(ngDevMode ? [{ debugName: "section" }] : /* istanbul ignore next */ []));
|
|
930
|
-
update = input.required(...(ngDevMode ? [{ debugName: "update" }] : /* istanbul ignore next */ []));
|
|
931
|
-
patch(changes) {
|
|
932
|
-
this.update()(changes);
|
|
933
|
-
}
|
|
934
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: BasicFormBuilderSectionSettings, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
935
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "21.2.4", type: BasicFormBuilderSectionSettings, isStandalone: true, selector: "ngs-basic-form-builder-section-settings", inputs: { section: { classPropertyName: "section", publicName: "section", isSignal: true, isRequired: true, transformFunction: null }, update: { classPropertyName: "update", publicName: "update", isSignal: true, isRequired: true, transformFunction: null } }, host: { classAttribute: "ngs-basic-form-builder-section-settings" }, exportAs: ["ngsBasicFormBuilderSectionSettings"], ngImport: i0, template: "<div class=\"flex flex-col gap-4\">\n <ngs-form-field>\n <ngs-label>Title</ngs-label>\n <input ngsInput\n [ngModel]=\"section().title\"\n [ngModelOptions]=\"{standalone: true}\"\n (ngModelChange)=\"patch({ title: $event })\">\n </ngs-form-field>\n\n <ngs-form-field>\n <ngs-label>Description</ngs-label>\n <textarea ngsInput\n rows=\"3\"\n [ngModel]=\"section().description || ''\"\n [ngModelOptions]=\"{standalone: true}\"\n (ngModelChange)=\"patch({ description: $event })\"></textarea>\n <ngs-hint>Optional helper text rendered under the section title.</ngs-hint>\n </ngs-form-field>\n\n <ngs-slide-toggle [ngModel]=\"section().collapsed ?? false\"\n [ngModelOptions]=\"{standalone: true}\"\n (ngModelChange)=\"patch({ collapsed: $event })\">\n Collapsed\n </ngs-slide-toggle>\n</div>\n", styles: [":host{display:block}\n/*! tailwindcss v4.2.2 | MIT License | https://tailwindcss.com */\n"], dependencies: [{ kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1.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: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "component", type: FormField, selector: "ngs-form-field", inputs: ["subscriptHiddenIfEmpty", "sameHeightAsButton"], exportAs: ["ngsFormField"] }, { kind: "component", type: Hint, selector: "ngs-hint", inputs: ["align"], exportAs: ["ngsHint"] }, { kind: "component", type: Label, selector: "ngs-label" }, { kind: "directive", type: Input, selector: "input[ngsInput], textarea[ngsInput]", inputs: ["id", "placeholder", "required", "disabled", "readonly", "errorStateMatcher"], exportAs: ["ngsInput"] }, { kind: "component", type: SlideToggle, selector: "ngs-slide-toggle", inputs: ["id", "name", "labelPosition", "aria-label", "aria-labelledby", "aria-describedby", "required", "disabled", "disableRipple", "tabIndex", "hideIcon", "color", "checked"], outputs: ["disabledChange", "checkedChange", "change", "toggleChange"], exportAs: ["ngsSlideToggle"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
936
|
-
}
|
|
937
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: BasicFormBuilderSectionSettings, decorators: [{
|
|
938
|
-
type: Component,
|
|
939
|
-
args: [{ selector: 'ngs-basic-form-builder-section-settings', exportAs: 'ngsBasicFormBuilderSectionSettings', imports: [
|
|
940
|
-
FormsModule,
|
|
941
|
-
FormField,
|
|
942
|
-
Hint,
|
|
943
|
-
Label,
|
|
944
|
-
Input,
|
|
945
|
-
SlideToggle
|
|
946
|
-
], changeDetection: ChangeDetectionStrategy.OnPush, host: {
|
|
947
|
-
'class': 'ngs-basic-form-builder-section-settings'
|
|
948
|
-
}, template: "<div class=\"flex flex-col gap-4\">\n <ngs-form-field>\n <ngs-label>Title</ngs-label>\n <input ngsInput\n [ngModel]=\"section().title\"\n [ngModelOptions]=\"{standalone: true}\"\n (ngModelChange)=\"patch({ title: $event })\">\n </ngs-form-field>\n\n <ngs-form-field>\n <ngs-label>Description</ngs-label>\n <textarea ngsInput\n rows=\"3\"\n [ngModel]=\"section().description || ''\"\n [ngModelOptions]=\"{standalone: true}\"\n (ngModelChange)=\"patch({ description: $event })\"></textarea>\n <ngs-hint>Optional helper text rendered under the section title.</ngs-hint>\n </ngs-form-field>\n\n <ngs-slide-toggle [ngModel]=\"section().collapsed ?? false\"\n [ngModelOptions]=\"{standalone: true}\"\n (ngModelChange)=\"patch({ collapsed: $event })\">\n Collapsed\n </ngs-slide-toggle>\n</div>\n", styles: [":host{display:block}\n/*! tailwindcss v4.2.2 | MIT License | https://tailwindcss.com */\n"] }]
|
|
949
|
-
}], propDecorators: { section: [{ type: i0.Input, args: [{ isSignal: true, alias: "section", required: true }] }], update: [{ type: i0.Input, args: [{ isSignal: true, alias: "update", required: true }] }] } });
|
|
950
|
-
|
|
951
1118
|
class FormBuilderSettingsHost {
|
|
952
1119
|
field = input(null, ...(ngDevMode ? [{ debugName: "field" }] : /* istanbul ignore next */ []));
|
|
953
1120
|
section = input(null, ...(ngDevMode ? [{ debugName: "section" }] : /* istanbul ignore next */ []));
|
|
@@ -964,10 +1131,29 @@ class FormBuilderSettingsHost {
|
|
|
964
1131
|
}
|
|
965
1132
|
return field ? this.definitions().find(definition => definition.type === field.type) : undefined;
|
|
966
1133
|
}, ...(ngDevMode ? [{ debugName: "itemDefinition" }] : /* istanbul ignore next */ []));
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
1134
|
+
settingsSchema = computed(() => {
|
|
1135
|
+
const definition = this.itemDefinition();
|
|
1136
|
+
const config = this.settingsConfig(definition);
|
|
1137
|
+
const inheritance = config?.extends ?? this.inferSettingsInheritance(definition);
|
|
1138
|
+
const schemas = [
|
|
1139
|
+
this.baseSettingsSchema(inheritance),
|
|
1140
|
+
this.resolveOwnSettingsSchema(config, definition)
|
|
1141
|
+
].filter((schema) => !!schema);
|
|
1142
|
+
return mergeSettingsSchemas(schemas);
|
|
1143
|
+
}, ...(ngDevMode ? [{ debugName: "settingsSchema" }] : /* istanbul ignore next */ []));
|
|
1144
|
+
settingsValue = computed(() => {
|
|
1145
|
+
const item = this.section() ?? this.field();
|
|
1146
|
+
const schema = this.settingsSchema();
|
|
1147
|
+
const value = {};
|
|
1148
|
+
if (!item || !schema) {
|
|
1149
|
+
return value;
|
|
1150
|
+
}
|
|
1151
|
+
for (const settingField of flattenSettingsFields(schema)) {
|
|
1152
|
+
value[settingField.name] = this.readSettingValue(item, settingField);
|
|
1153
|
+
}
|
|
1154
|
+
return value;
|
|
1155
|
+
}, ...(ngDevMode ? [{ debugName: "settingsValue" }] : /* istanbul ignore next */ []));
|
|
1156
|
+
legacyLoaded = signal(false, ...(ngDevMode ? [{ debugName: "legacyLoaded" }] : /* istanbul ignore next */ []));
|
|
971
1157
|
anchor = viewChild.required('anchor', { read: ViewContainerRef });
|
|
972
1158
|
constructor() {
|
|
973
1159
|
effect(async () => {
|
|
@@ -979,18 +1165,18 @@ class FormBuilderSettingsHost {
|
|
|
979
1165
|
const type = section ? 'section' : field?.type;
|
|
980
1166
|
const itemDefinition = type ? definitions.find(definition => definition.type === type) : undefined;
|
|
981
1167
|
const kind = section ? 'layout' : (itemDefinition?.kind ?? field?.kind ?? 'field');
|
|
982
|
-
const
|
|
1168
|
+
const legacySettings = this.legacySettingsImporter(itemDefinition) ?? settingDefinitions.find(definition => {
|
|
983
1169
|
if (type && (definition.itemType === type || definition.fieldType === type || definition.type === type)) {
|
|
984
1170
|
return true;
|
|
985
1171
|
}
|
|
986
1172
|
return !!definition.kind && definition.kind === kind;
|
|
987
1173
|
})?.component;
|
|
988
1174
|
viewContainer.clear();
|
|
989
|
-
this.
|
|
990
|
-
if (!
|
|
1175
|
+
this.legacyLoaded.set(false);
|
|
1176
|
+
if (!legacySettings) {
|
|
991
1177
|
return;
|
|
992
1178
|
}
|
|
993
|
-
const componentType = await
|
|
1179
|
+
const componentType = await legacySettings();
|
|
994
1180
|
const componentRef = viewContainer.createComponent(componentType);
|
|
995
1181
|
componentRef.setInput('item', section ?? field);
|
|
996
1182
|
componentRef.setInput('field', field);
|
|
@@ -1000,23 +1186,286 @@ class FormBuilderSettingsHost {
|
|
|
1000
1186
|
componentRef.setInput('update', section ? this.updateSection() : this.update());
|
|
1001
1187
|
componentRef.setInput('updateField', this.update());
|
|
1002
1188
|
componentRef.setInput('updateSection', this.updateSection());
|
|
1003
|
-
this.
|
|
1189
|
+
this.legacyLoaded.set(true);
|
|
1004
1190
|
});
|
|
1005
1191
|
}
|
|
1006
|
-
|
|
1007
|
-
|
|
1192
|
+
applySettingsValue(value) {
|
|
1193
|
+
const schema = this.settingsSchema();
|
|
1194
|
+
const section = this.section();
|
|
1195
|
+
const field = this.field();
|
|
1196
|
+
if (!schema) {
|
|
1197
|
+
return;
|
|
1198
|
+
}
|
|
1199
|
+
if (section) {
|
|
1200
|
+
const patch = this.createSettingsPatch(section, schema, value);
|
|
1201
|
+
if (Object.keys(patch).length) {
|
|
1202
|
+
this.updateSection()?.(patch);
|
|
1203
|
+
}
|
|
1204
|
+
return;
|
|
1205
|
+
}
|
|
1206
|
+
if (field) {
|
|
1207
|
+
const patch = this.createSettingsPatch(field, schema, value);
|
|
1208
|
+
if (Object.keys(patch).length) {
|
|
1209
|
+
this.update()?.(patch);
|
|
1210
|
+
}
|
|
1211
|
+
}
|
|
1212
|
+
}
|
|
1213
|
+
createSettingsPatch(item, schema, value) {
|
|
1214
|
+
const patch = {};
|
|
1215
|
+
for (const settingField of flattenSettingsFields(schema)) {
|
|
1216
|
+
if (settingField.settings?.['valueAdapter'] === 'optionsText' && isFormBuilderField(item)) {
|
|
1217
|
+
if (value[settingField.name] === this.readSettingValue(item, settingField)) {
|
|
1218
|
+
continue;
|
|
1219
|
+
}
|
|
1220
|
+
const nextOptions = parseOptionsText(String(value[settingField.name] ?? ''), item);
|
|
1221
|
+
patch['options'] = nextOptions;
|
|
1222
|
+
patch['defaultValue'] = resolveSelectedDefaultValue(nextOptions, item);
|
|
1223
|
+
continue;
|
|
1224
|
+
}
|
|
1225
|
+
if (settingField.settings?.['valueAdapter'] === 'selectMultiple' && isFormBuilderField(item)) {
|
|
1226
|
+
const multiple = value[settingField.name] === true;
|
|
1227
|
+
if (multiple === (item.multiple === true)) {
|
|
1228
|
+
continue;
|
|
1229
|
+
}
|
|
1230
|
+
const options = multiple ? item.options ?? [] : normalizeSelectedOptions(item.options ?? [], { ...item, multiple });
|
|
1231
|
+
patch['multiple'] = multiple;
|
|
1232
|
+
patch['options'] = options;
|
|
1233
|
+
patch['defaultValue'] = resolveMultipleDefaultValue(item, options, multiple);
|
|
1234
|
+
continue;
|
|
1235
|
+
}
|
|
1236
|
+
if (settingField.settings?.['valueAdapter'] === 'multipleDefaultValue' && isFormBuilderField(item)) {
|
|
1237
|
+
const multiple = value[settingField.name] === true;
|
|
1238
|
+
if (multiple === (item.multiple === true)) {
|
|
1239
|
+
continue;
|
|
1240
|
+
}
|
|
1241
|
+
patch['multiple'] = multiple;
|
|
1242
|
+
patch['defaultValue'] = multiple ? [] : null;
|
|
1243
|
+
continue;
|
|
1244
|
+
}
|
|
1245
|
+
const nextValue = value[settingField.name];
|
|
1246
|
+
if (nextValue === this.readSettingValue(item, settingField)) {
|
|
1247
|
+
continue;
|
|
1248
|
+
}
|
|
1249
|
+
seedPathPatch(patch, item, settingField.name);
|
|
1250
|
+
setPathValue(patch, settingField.name, nextValue);
|
|
1251
|
+
}
|
|
1252
|
+
return patch;
|
|
1253
|
+
}
|
|
1254
|
+
readSettingValue(item, settingField) {
|
|
1255
|
+
if (settingField.settings?.['valueAdapter'] === 'optionsText' && isFormBuilderField(item)) {
|
|
1256
|
+
return optionsToText(item.options ?? [], item.defaultValue);
|
|
1257
|
+
}
|
|
1258
|
+
const value = getPathValue(item, settingField.name);
|
|
1259
|
+
return value === undefined ? settingField.defaultValue ?? null : value;
|
|
1260
|
+
}
|
|
1261
|
+
settingsConfig(definition) {
|
|
1262
|
+
return definition?.settings && typeof definition.settings !== 'function'
|
|
1263
|
+
? definition.settings
|
|
1264
|
+
: undefined;
|
|
1265
|
+
}
|
|
1266
|
+
legacySettingsImporter(definition) {
|
|
1267
|
+
return typeof definition?.settings === 'function'
|
|
1268
|
+
? definition.settings
|
|
1269
|
+
: undefined;
|
|
1270
|
+
}
|
|
1271
|
+
resolveOwnSettingsSchema(config, definition) {
|
|
1272
|
+
const schema = config?.schema;
|
|
1273
|
+
if (!schema) {
|
|
1274
|
+
return null;
|
|
1275
|
+
}
|
|
1276
|
+
if (typeof schema !== 'function') {
|
|
1277
|
+
return schema;
|
|
1278
|
+
}
|
|
1279
|
+
const section = this.section();
|
|
1280
|
+
if (section) {
|
|
1281
|
+
return schema({
|
|
1282
|
+
item: section,
|
|
1283
|
+
section,
|
|
1284
|
+
schema: this.schema(),
|
|
1285
|
+
definition,
|
|
1286
|
+
update: this.updateSection(),
|
|
1287
|
+
updateSection: this.updateSection()
|
|
1288
|
+
});
|
|
1289
|
+
}
|
|
1290
|
+
const field = this.field();
|
|
1291
|
+
return schema({
|
|
1292
|
+
item: field,
|
|
1293
|
+
field,
|
|
1294
|
+
schema: this.schema(),
|
|
1295
|
+
definition,
|
|
1296
|
+
update: this.update(),
|
|
1297
|
+
updateField: this.update()
|
|
1298
|
+
});
|
|
1299
|
+
}
|
|
1300
|
+
inferSettingsInheritance(definition) {
|
|
1301
|
+
const field = this.field();
|
|
1302
|
+
if (this.section()) {
|
|
1303
|
+
return 'none';
|
|
1304
|
+
}
|
|
1305
|
+
const kind = definition?.kind ?? field?.kind ?? 'field';
|
|
1306
|
+
if (kind === 'static') {
|
|
1307
|
+
return 'static';
|
|
1308
|
+
}
|
|
1309
|
+
if (kind === 'layout' || definition?.acceptsChildren || field?.children?.length) {
|
|
1310
|
+
return 'layout';
|
|
1311
|
+
}
|
|
1312
|
+
return 'field';
|
|
1313
|
+
}
|
|
1314
|
+
baseSettingsSchema(inheritance) {
|
|
1315
|
+
if (this.section()) {
|
|
1316
|
+
return inheritance === 'none' ? null : FORM_BUILDER_SECTION_BASE_SETTINGS_SCHEMA;
|
|
1317
|
+
}
|
|
1318
|
+
switch (inheritance) {
|
|
1319
|
+
case 'field':
|
|
1320
|
+
return FORM_BUILDER_FIELD_BASE_SETTINGS_SCHEMA;
|
|
1321
|
+
case 'input-field':
|
|
1322
|
+
return FORM_BUILDER_INPUT_FIELD_BASE_SETTINGS_SCHEMA;
|
|
1323
|
+
case 'layout':
|
|
1324
|
+
return FORM_BUILDER_LAYOUT_BASE_SETTINGS_SCHEMA;
|
|
1325
|
+
case 'layout-container':
|
|
1326
|
+
return FORM_BUILDER_LAYOUT_CONTAINER_BASE_SETTINGS_SCHEMA;
|
|
1327
|
+
case 'static':
|
|
1328
|
+
return FORM_BUILDER_STATIC_BASE_SETTINGS_SCHEMA;
|
|
1329
|
+
default:
|
|
1330
|
+
return null;
|
|
1331
|
+
}
|
|
1332
|
+
}
|
|
1333
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: FormBuilderSettingsHost, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
1334
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.4", type: FormBuilderSettingsHost, isStandalone: true, selector: "ngs-form-builder-settings-host", inputs: { field: { classPropertyName: "field", publicName: "field", isSignal: true, isRequired: false, transformFunction: null }, section: { classPropertyName: "section", publicName: "section", isSignal: true, isRequired: false, transformFunction: null }, schema: { classPropertyName: "schema", publicName: "schema", isSignal: true, isRequired: true, transformFunction: null }, definitions: { classPropertyName: "definitions", publicName: "definitions", isSignal: true, isRequired: false, transformFunction: null }, settingsDefinitions: { classPropertyName: "settingsDefinitions", publicName: "settingsDefinitions", isSignal: true, isRequired: false, transformFunction: null }, update: { classPropertyName: "update", publicName: "update", isSignal: true, isRequired: false, transformFunction: null }, updateSection: { classPropertyName: "updateSection", publicName: "updateSection", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "class.is-empty": "!settingsSchema() && !legacyLoaded()" }, classAttribute: "ngs-form-builder-settings-host" }, viewQueries: [{ propertyName: "anchor", first: true, predicate: ["anchor"], descendants: true, read: ViewContainerRef, isSignal: true }], exportAs: ["ngsFormBuilderSettingsHost"], ngImport: i0, template: "@if (settingsSchema(); as schema) {\n <ngs-form-renderer\n [schema]=\"schema\"\n [value]=\"settingsValue()\"\n [showSubmit]=\"false\"\n (valueChange)=\"applySettingsValue($event)\"/>\n}\n\n<ng-container #anchor/>\n", dependencies: [{ kind: "component", type: FormBuilderRenderer, selector: "ngs-form-renderer", inputs: ["schema", "readonly", "showSubmit", "submitLabel", "uploadCallback", "value"], outputs: ["valueChange", "formSubmit", "formReady"], exportAs: ["ngsFormRenderer"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
1335
|
+
}
|
|
1336
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: FormBuilderSettingsHost, decorators: [{
|
|
1337
|
+
type: Component,
|
|
1338
|
+
args: [{ selector: 'ngs-form-builder-settings-host', exportAs: 'ngsFormBuilderSettingsHost', imports: [
|
|
1339
|
+
FormBuilderRenderer
|
|
1340
|
+
], changeDetection: ChangeDetectionStrategy.OnPush, host: {
|
|
1341
|
+
'class': 'ngs-form-builder-settings-host',
|
|
1342
|
+
'[class.is-empty]': '!settingsSchema() && !legacyLoaded()'
|
|
1343
|
+
}, template: "@if (settingsSchema(); as schema) {\n <ngs-form-renderer\n [schema]=\"schema\"\n [value]=\"settingsValue()\"\n [showSubmit]=\"false\"\n (valueChange)=\"applySettingsValue($event)\"/>\n}\n\n<ng-container #anchor/>\n" }]
|
|
1344
|
+
}], ctorParameters: () => [], propDecorators: { field: [{ type: i0.Input, args: [{ isSignal: true, alias: "field", required: false }] }], section: [{ type: i0.Input, args: [{ isSignal: true, alias: "section", required: false }] }], schema: [{ type: i0.Input, args: [{ isSignal: true, alias: "schema", required: true }] }], definitions: [{ type: i0.Input, args: [{ isSignal: true, alias: "definitions", required: false }] }], settingsDefinitions: [{ type: i0.Input, args: [{ isSignal: true, alias: "settingsDefinitions", required: false }] }], update: [{ type: i0.Input, args: [{ isSignal: true, alias: "update", required: false }] }], updateSection: [{ type: i0.Input, args: [{ isSignal: true, alias: "updateSection", required: false }] }], anchor: [{ type: i0.ViewChild, args: ['anchor', { ...{ read: ViewContainerRef }, isSignal: true }] }] } });
|
|
1345
|
+
function mergeSettingsSchemas(schemas) {
|
|
1346
|
+
if (!schemas.length) {
|
|
1347
|
+
return null;
|
|
1348
|
+
}
|
|
1349
|
+
return {
|
|
1350
|
+
sections: schemas.flatMap(schema => schema.sections.map(section => ({
|
|
1351
|
+
...section,
|
|
1352
|
+
fields: section.fields.map(field => ({ ...field }))
|
|
1353
|
+
})))
|
|
1354
|
+
};
|
|
1355
|
+
}
|
|
1356
|
+
function flattenSettingsFields(schema) {
|
|
1357
|
+
return [
|
|
1358
|
+
...(schema.fields ?? []),
|
|
1359
|
+
...schema.sections.flatMap(section => section.fields)
|
|
1360
|
+
].flatMap(field => [field, ...flattenSettingsFields({ sections: [], fields: field.children ?? [] })]);
|
|
1361
|
+
}
|
|
1362
|
+
function getPathValue(source, path) {
|
|
1363
|
+
return path.split('.').reduce((value, key) => value?.[key], source);
|
|
1364
|
+
}
|
|
1365
|
+
function setPathValue(target, path, value) {
|
|
1366
|
+
const keys = path.split('.');
|
|
1367
|
+
let cursor = target;
|
|
1368
|
+
keys.slice(0, -1).forEach(key => {
|
|
1369
|
+
cursor[key] = cursor[key] ?? {};
|
|
1370
|
+
cursor = cursor[key];
|
|
1371
|
+
});
|
|
1372
|
+
cursor[keys[keys.length - 1]] = value;
|
|
1373
|
+
}
|
|
1374
|
+
function seedPathPatch(target, source, path) {
|
|
1375
|
+
const [rootKey] = path.split('.');
|
|
1376
|
+
const sourceValue = source[rootKey];
|
|
1377
|
+
if (!path.includes('.') || target[rootKey] !== undefined || !isPlainObject(sourceValue)) {
|
|
1378
|
+
return;
|
|
1379
|
+
}
|
|
1380
|
+
target[rootKey] = { ...sourceValue };
|
|
1381
|
+
}
|
|
1382
|
+
function isPlainObject(value) {
|
|
1383
|
+
return typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
1384
|
+
}
|
|
1385
|
+
function isFormBuilderField(item) {
|
|
1386
|
+
return 'type' in item;
|
|
1387
|
+
}
|
|
1388
|
+
function optionsToText(options, defaultValue) {
|
|
1389
|
+
return options
|
|
1390
|
+
.map(option => `${option.label}:${option.value}${isOptionSelected(option, defaultValue) ? ':selected' : ''}`)
|
|
1391
|
+
.join('\n');
|
|
1392
|
+
}
|
|
1393
|
+
function parseOptionsText(value, field) {
|
|
1394
|
+
return normalizeSelectedOptions(value
|
|
1395
|
+
.split('\n')
|
|
1396
|
+
.map(line => line.trim())
|
|
1397
|
+
.filter(Boolean)
|
|
1398
|
+
.map((line, index) => {
|
|
1399
|
+
const parsed = parseOptionLine$1(line);
|
|
1400
|
+
return {
|
|
1401
|
+
label: parsed?.label || `Option ${index + 1}`,
|
|
1402
|
+
value: parsed?.value || `option_${index + 1}`,
|
|
1403
|
+
selected: parsed?.selected || undefined
|
|
1404
|
+
};
|
|
1405
|
+
}), field);
|
|
1406
|
+
}
|
|
1407
|
+
function parseOptionLine$1(line) {
|
|
1408
|
+
const parts = line.split(':').map(part => part.trim());
|
|
1409
|
+
if (parts.length < 2 || !parts[0] || !parts[1]) {
|
|
1410
|
+
return null;
|
|
1411
|
+
}
|
|
1412
|
+
return {
|
|
1413
|
+
label: parts[0],
|
|
1414
|
+
value: parts[1],
|
|
1415
|
+
selected: parts[2] === 'selected'
|
|
1416
|
+
};
|
|
1417
|
+
}
|
|
1418
|
+
function normalizeSelectedOptions(options, field) {
|
|
1419
|
+
if (field.type === 'checkbox-list' || field.multiple) {
|
|
1420
|
+
return options;
|
|
1421
|
+
}
|
|
1422
|
+
let selectedSeen = false;
|
|
1423
|
+
return options.map(option => {
|
|
1424
|
+
if (!option.selected) {
|
|
1425
|
+
return option;
|
|
1426
|
+
}
|
|
1427
|
+
if (selectedSeen) {
|
|
1428
|
+
return {
|
|
1429
|
+
...option,
|
|
1430
|
+
selected: undefined
|
|
1431
|
+
};
|
|
1432
|
+
}
|
|
1433
|
+
selectedSeen = true;
|
|
1434
|
+
return option;
|
|
1435
|
+
});
|
|
1436
|
+
}
|
|
1437
|
+
function resolveSelectedDefaultValue(options, field) {
|
|
1438
|
+
const selectedValues = options
|
|
1439
|
+
.filter(option => option.selected)
|
|
1440
|
+
.map(option => option.value);
|
|
1441
|
+
if (field.type === 'checkbox-list' || field.multiple) {
|
|
1442
|
+
return selectedValues;
|
|
1443
|
+
}
|
|
1444
|
+
return selectedValues[0] ?? null;
|
|
1445
|
+
}
|
|
1446
|
+
function resolveMultipleDefaultValue(field, options, multiple) {
|
|
1447
|
+
const selectedValues = options
|
|
1448
|
+
.filter(option => option.selected)
|
|
1449
|
+
.map(option => option.value);
|
|
1450
|
+
if (multiple) {
|
|
1451
|
+
return Array.isArray(field.defaultValue)
|
|
1452
|
+
? field.defaultValue
|
|
1453
|
+
: field.defaultValue == null
|
|
1454
|
+
? selectedValues
|
|
1455
|
+
: [field.defaultValue];
|
|
1456
|
+
}
|
|
1457
|
+
return Array.isArray(field.defaultValue)
|
|
1458
|
+
? field.defaultValue[0] ?? selectedValues[0] ?? null
|
|
1459
|
+
: field.defaultValue ?? selectedValues[0] ?? null;
|
|
1460
|
+
}
|
|
1461
|
+
function isOptionSelected(option, defaultValue) {
|
|
1462
|
+
if (option.selected) {
|
|
1463
|
+
return true;
|
|
1464
|
+
}
|
|
1465
|
+
return Array.isArray(defaultValue)
|
|
1466
|
+
? defaultValue.includes(option.value)
|
|
1467
|
+
: defaultValue === option.value;
|
|
1008
1468
|
}
|
|
1009
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: FormBuilderSettingsHost, decorators: [{
|
|
1010
|
-
type: Component,
|
|
1011
|
-
args: [{ selector: 'ngs-form-builder-settings-host', exportAs: 'ngsFormBuilderSettingsHost', imports: [
|
|
1012
|
-
BasicFormBuilderFieldSettings,
|
|
1013
|
-
BasicFormBuilderLayoutSettings,
|
|
1014
|
-
BasicFormBuilderSectionSettings
|
|
1015
|
-
], changeDetection: ChangeDetectionStrategy.OnPush, host: {
|
|
1016
|
-
'class': 'ngs-form-builder-settings-host',
|
|
1017
|
-
'[class.is-empty]': '!customLoaded()'
|
|
1018
|
-
}, template: "@if (section(); as sectionItem) {\n <ngs-basic-form-builder-section-settings\n [section]=\"sectionItem\"\n [update]=\"updateSection()!\"/>\n} @else if (field(); as fieldItem) {\n @if (isLayoutField()) {\n <ngs-basic-form-builder-layout-settings\n [field]=\"fieldItem\"\n [update]=\"update()!\"/>\n } @else {\n <ngs-basic-form-builder-field-settings\n [field]=\"fieldItem\"\n [update]=\"update()!\"/>\n }\n}\n\n<ng-container #anchor/>\n" }]
|
|
1019
|
-
}], ctorParameters: () => [], propDecorators: { field: [{ type: i0.Input, args: [{ isSignal: true, alias: "field", required: false }] }], section: [{ type: i0.Input, args: [{ isSignal: true, alias: "section", required: false }] }], schema: [{ type: i0.Input, args: [{ isSignal: true, alias: "schema", required: true }] }], definitions: [{ type: i0.Input, args: [{ isSignal: true, alias: "definitions", required: false }] }], settingsDefinitions: [{ type: i0.Input, args: [{ isSignal: true, alias: "settingsDefinitions", required: false }] }], update: [{ type: i0.Input, args: [{ isSignal: true, alias: "update", required: false }] }], updateSection: [{ type: i0.Input, args: [{ isSignal: true, alias: "updateSection", required: false }] }], anchor: [{ type: i0.ViewChild, args: ['anchor', { ...{ read: ViewContainerRef }, isSignal: true }] }] } });
|
|
1020
1469
|
|
|
1021
1470
|
const ROOT_DROP_LIST_ID = 'ngs-form-builder-root-fields';
|
|
1022
1471
|
const PALETTE_DRAG_TYPE = 'application/x-ngstarter-form-builder-field';
|
|
@@ -1123,7 +1572,7 @@ class FormBuilder {
|
|
|
1123
1572
|
return this.fieldTreeRootNodes;
|
|
1124
1573
|
}, ...(ngDevMode ? [{ debugName: "fieldTree" }] : /* istanbul ignore next */ []));
|
|
1125
1574
|
fieldTreeChildrenAccessor = (node) => node.children ?? [];
|
|
1126
|
-
hasFieldTreeChildren = (_, node) => !!node.children
|
|
1575
|
+
hasFieldTreeChildren = (_, node) => !!node.children;
|
|
1127
1576
|
trackFieldTreeNode = (_, node) => node.id;
|
|
1128
1577
|
fieldTreeDraggablePredicate = (_node) => true;
|
|
1129
1578
|
fieldTreeDropPredicate = (source, target, position) => this.canDropFieldTreeNode(source, target, position);
|
|
@@ -1341,7 +1790,7 @@ class FormBuilder {
|
|
|
1341
1790
|
}
|
|
1342
1791
|
toggleFieldTreeNode(tree, node, event) {
|
|
1343
1792
|
event.stopPropagation();
|
|
1344
|
-
if (!node.children
|
|
1793
|
+
if (!node.children) {
|
|
1345
1794
|
return;
|
|
1346
1795
|
}
|
|
1347
1796
|
if (tree.isExpanded(node)) {
|
|
@@ -1875,7 +2324,7 @@ class FormBuilder {
|
|
|
1875
2324
|
return;
|
|
1876
2325
|
}
|
|
1877
2326
|
for (const node of this.flattenFieldTree(this.fieldTree())) {
|
|
1878
|
-
if (node.children
|
|
2327
|
+
if (node.children && expandedIds.has(node.id)) {
|
|
1879
2328
|
tree.expand(node);
|
|
1880
2329
|
}
|
|
1881
2330
|
}
|
|
@@ -1899,7 +2348,7 @@ class FormBuilder {
|
|
|
1899
2348
|
}
|
|
1900
2349
|
const expandableIds = path
|
|
1901
2350
|
.slice(0, -1)
|
|
1902
|
-
.filter(node => !!node.children
|
|
2351
|
+
.filter(node => !!node.children)
|
|
1903
2352
|
.map(node => node.id);
|
|
1904
2353
|
if (expandableIds.length) {
|
|
1905
2354
|
this.expandedFieldTreeNodeIds.update(ids => {
|
|
@@ -1915,7 +2364,7 @@ class FormBuilder {
|
|
|
1915
2364
|
const tree = this.actualFieldsTree();
|
|
1916
2365
|
if (tree) {
|
|
1917
2366
|
for (const node of path.slice(0, -1)) {
|
|
1918
|
-
if (node.children
|
|
2367
|
+
if (node.children) {
|
|
1919
2368
|
tree.expand(node);
|
|
1920
2369
|
}
|
|
1921
2370
|
}
|
|
@@ -2075,9 +2524,9 @@ class FormBuilder {
|
|
|
2075
2524
|
node.kind = 'field';
|
|
2076
2525
|
node.field = field;
|
|
2077
2526
|
node.section = section;
|
|
2078
|
-
if (children?.length) {
|
|
2527
|
+
if (children?.length || this.isContainerField(field)) {
|
|
2079
2528
|
node.children ??= [];
|
|
2080
|
-
replaceArrayContents(node.children, children);
|
|
2529
|
+
replaceArrayContents(node.children, children ?? []);
|
|
2081
2530
|
}
|
|
2082
2531
|
else {
|
|
2083
2532
|
node.children = undefined;
|
|
@@ -2306,9 +2755,253 @@ function containsField(field, fieldId) {
|
|
|
2306
2755
|
return (field.children ?? []).some(child => child.id === fieldId || containsField(child, fieldId));
|
|
2307
2756
|
}
|
|
2308
2757
|
|
|
2758
|
+
class BasicFormBuilderFieldSettings {
|
|
2759
|
+
destroyRef = inject(DestroyRef);
|
|
2760
|
+
field = input.required(...(ngDevMode ? [{ debugName: "field" }] : /* istanbul ignore next */ []));
|
|
2761
|
+
update = input.required(...(ngDevMode ? [{ debugName: "update" }] : /* istanbul ignore next */ []));
|
|
2762
|
+
optionsControl = new FormControl('', {
|
|
2763
|
+
nonNullable: true,
|
|
2764
|
+
validators: optionsTextValidator
|
|
2765
|
+
});
|
|
2766
|
+
fieldWidthOptions = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12];
|
|
2767
|
+
radioOrientationOptions = ['vertical', 'horizontal'];
|
|
2768
|
+
spacerHeightOptions = [8, 16, 24, 32, 48, 64];
|
|
2769
|
+
hasCheckedState = computed(() => ['checkbox', 'toggle'].includes(this.field().type), ...(ngDevMode ? [{ debugName: "hasCheckedState" }] : /* istanbul ignore next */ []));
|
|
2770
|
+
hasPlaceholder = computed(() => !['checkbox', 'toggle', 'radio', 'spacer'].includes(this.field().type), ...(ngDevMode ? [{ debugName: "hasPlaceholder" }] : /* istanbul ignore next */ []));
|
|
2771
|
+
hasOptions = computed(() => ['select', 'radio', 'checkbox-list'].includes(this.field().type), ...(ngDevMode ? [{ debugName: "hasOptions" }] : /* istanbul ignore next */ []));
|
|
2772
|
+
hasClearable = computed(() => ['select', 'country-select'].includes(this.field().type), ...(ngDevMode ? [{ debugName: "hasClearable" }] : /* istanbul ignore next */ []));
|
|
2773
|
+
hasMultiple = computed(() => ['select', 'upload'].includes(this.field().type), ...(ngDevMode ? [{ debugName: "hasMultiple" }] : /* istanbul ignore next */ []));
|
|
2774
|
+
isSelect = computed(() => this.field().type === 'select', ...(ngDevMode ? [{ debugName: "isSelect" }] : /* istanbul ignore next */ []));
|
|
2775
|
+
isUpload = computed(() => this.field().type === 'upload', ...(ngDevMode ? [{ debugName: "isUpload" }] : /* istanbul ignore next */ []));
|
|
2776
|
+
isRadio = computed(() => this.field().type === 'radio', ...(ngDevMode ? [{ debugName: "isRadio" }] : /* istanbul ignore next */ []));
|
|
2777
|
+
isSpacer = computed(() => this.field().type === 'spacer', ...(ngDevMode ? [{ debugName: "isSpacer" }] : /* istanbul ignore next */ []));
|
|
2778
|
+
hasBehaviorToggles = computed(() => !this.isSpacer(), ...(ngDevMode ? [{ debugName: "hasBehaviorToggles" }] : /* istanbul ignore next */ []));
|
|
2779
|
+
radioOrientation = computed(() => this.field().settings?.['orientation'] === 'horizontal' ? 'horizontal' : 'vertical', ...(ngDevMode ? [{ debugName: "radioOrientation" }] : /* istanbul ignore next */ []));
|
|
2780
|
+
optionsText = computed(() => (this.field().options ?? [])
|
|
2781
|
+
.map(option => `${option.label}:${option.value}${this.isOptionSelected(option) ? ':selected' : ''}`)
|
|
2782
|
+
.join('\n'), ...(ngDevMode ? [{ debugName: "optionsText" }] : /* istanbul ignore next */ []));
|
|
2783
|
+
constructor() {
|
|
2784
|
+
effect(() => {
|
|
2785
|
+
const nextValue = this.optionsText();
|
|
2786
|
+
if (this.optionsControl.value !== nextValue) {
|
|
2787
|
+
this.optionsControl.setValue(nextValue, { emitEvent: false });
|
|
2788
|
+
}
|
|
2789
|
+
});
|
|
2790
|
+
this.optionsControl.valueChanges
|
|
2791
|
+
.pipe(takeUntilDestroyed(this.destroyRef))
|
|
2792
|
+
.subscribe(value => {
|
|
2793
|
+
if (this.optionsControl.invalid) {
|
|
2794
|
+
return;
|
|
2795
|
+
}
|
|
2796
|
+
this.patchOptions(value);
|
|
2797
|
+
});
|
|
2798
|
+
}
|
|
2799
|
+
patch(changes) {
|
|
2800
|
+
this.update()(changes);
|
|
2801
|
+
}
|
|
2802
|
+
patchSettings(changes) {
|
|
2803
|
+
this.patch({
|
|
2804
|
+
settings: {
|
|
2805
|
+
...this.field().settings,
|
|
2806
|
+
...changes
|
|
2807
|
+
}
|
|
2808
|
+
});
|
|
2809
|
+
}
|
|
2810
|
+
patchMultiple(multiple) {
|
|
2811
|
+
if (this.field().type === 'select') {
|
|
2812
|
+
this.patchSelectMultiple(multiple);
|
|
2813
|
+
return;
|
|
2814
|
+
}
|
|
2815
|
+
this.patch({
|
|
2816
|
+
multiple,
|
|
2817
|
+
defaultValue: multiple ? [] : null
|
|
2818
|
+
});
|
|
2819
|
+
}
|
|
2820
|
+
patchSelectMultiple(multiple) {
|
|
2821
|
+
const field = this.field();
|
|
2822
|
+
const options = multiple ? field.options ?? [] : this.normalizeSelectedOptions(field.options ?? [], false);
|
|
2823
|
+
const selectedValues = this.selectedOptionValues(options);
|
|
2824
|
+
let defaultValue = null;
|
|
2825
|
+
if (multiple) {
|
|
2826
|
+
defaultValue = Array.isArray(field.defaultValue)
|
|
2827
|
+
? field.defaultValue
|
|
2828
|
+
: field.defaultValue == null
|
|
2829
|
+
? selectedValues
|
|
2830
|
+
: [field.defaultValue];
|
|
2831
|
+
}
|
|
2832
|
+
else {
|
|
2833
|
+
defaultValue = Array.isArray(field.defaultValue)
|
|
2834
|
+
? field.defaultValue[0] ?? selectedValues[0] ?? null
|
|
2835
|
+
: field.defaultValue ?? selectedValues[0] ?? null;
|
|
2836
|
+
}
|
|
2837
|
+
this.patch({ multiple, options, defaultValue });
|
|
2838
|
+
}
|
|
2839
|
+
patchRadioOrientation(orientation) {
|
|
2840
|
+
this.patch({
|
|
2841
|
+
settings: {
|
|
2842
|
+
...this.field().settings,
|
|
2843
|
+
orientation
|
|
2844
|
+
}
|
|
2845
|
+
});
|
|
2846
|
+
}
|
|
2847
|
+
patchOptions(value) {
|
|
2848
|
+
const options = this.normalizeSelectedOptions(value
|
|
2849
|
+
.split('\n')
|
|
2850
|
+
.map(line => line.trim())
|
|
2851
|
+
.filter(Boolean)
|
|
2852
|
+
.map((line, index) => {
|
|
2853
|
+
const parsed = parseOptionLine(line);
|
|
2854
|
+
return {
|
|
2855
|
+
label: parsed?.label || `Option ${index + 1}`,
|
|
2856
|
+
value: parsed?.value || `option_${index + 1}`,
|
|
2857
|
+
selected: parsed?.selected || undefined
|
|
2858
|
+
};
|
|
2859
|
+
}));
|
|
2860
|
+
this.patch({
|
|
2861
|
+
options,
|
|
2862
|
+
defaultValue: this.resolveSelectedDefaultValue(options)
|
|
2863
|
+
});
|
|
2864
|
+
}
|
|
2865
|
+
normalizeSelectedOptions(options, multiple = this.field().multiple) {
|
|
2866
|
+
if (this.field().type === 'checkbox-list' || multiple) {
|
|
2867
|
+
return options;
|
|
2868
|
+
}
|
|
2869
|
+
let selectedSeen = false;
|
|
2870
|
+
return options.map(option => {
|
|
2871
|
+
if (!option.selected) {
|
|
2872
|
+
return option;
|
|
2873
|
+
}
|
|
2874
|
+
if (selectedSeen) {
|
|
2875
|
+
return {
|
|
2876
|
+
...option,
|
|
2877
|
+
selected: undefined
|
|
2878
|
+
};
|
|
2879
|
+
}
|
|
2880
|
+
selectedSeen = true;
|
|
2881
|
+
return option;
|
|
2882
|
+
});
|
|
2883
|
+
}
|
|
2884
|
+
resolveSelectedDefaultValue(options) {
|
|
2885
|
+
const selectedValues = this.selectedOptionValues(options);
|
|
2886
|
+
if (this.field().type === 'checkbox-list' || this.field().multiple) {
|
|
2887
|
+
return selectedValues;
|
|
2888
|
+
}
|
|
2889
|
+
return selectedValues[0] ?? null;
|
|
2890
|
+
}
|
|
2891
|
+
selectedOptionValues(options) {
|
|
2892
|
+
return options
|
|
2893
|
+
.filter(option => option.selected)
|
|
2894
|
+
.map(option => option.value);
|
|
2895
|
+
}
|
|
2896
|
+
isOptionSelected(option) {
|
|
2897
|
+
if (option.selected) {
|
|
2898
|
+
return true;
|
|
2899
|
+
}
|
|
2900
|
+
const defaultValue = this.field().defaultValue;
|
|
2901
|
+
return Array.isArray(defaultValue)
|
|
2902
|
+
? defaultValue.includes(option.value)
|
|
2903
|
+
: defaultValue === option.value;
|
|
2904
|
+
}
|
|
2905
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: BasicFormBuilderFieldSettings, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
2906
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.4", type: BasicFormBuilderFieldSettings, isStandalone: true, selector: "ngs-basic-form-builder-field-settings", inputs: { field: { classPropertyName: "field", publicName: "field", isSignal: true, isRequired: true, transformFunction: null }, update: { classPropertyName: "update", publicName: "update", isSignal: true, isRequired: true, transformFunction: null } }, host: { classAttribute: "ngs-basic-form-builder-field-settings" }, exportAs: ["ngsBasicFormBuilderFieldSettings"], ngImport: i0, template: "<div class=\"flex flex-col gap-4\">\n <ngs-form-field>\n <ngs-label>Label</ngs-label>\n <input ngsInput\n [ngModel]=\"field().label\"\n [ngModelOptions]=\"{standalone: true}\"\n (ngModelChange)=\"patch({ label: $event })\">\n </ngs-form-field>\n\n @if (!isSpacer()) {\n <ngs-form-field>\n <ngs-label>Field ID</ngs-label>\n <input ngsInput\n [ngModel]=\"field().name\"\n [ngModelOptions]=\"{standalone: true}\"\n (ngModelChange)=\"patch({ name: $event })\">\n </ngs-form-field>\n }\n\n @if (hasPlaceholder()) {\n <ngs-form-field>\n <ngs-label>Placeholder</ngs-label>\n <input ngsInput\n [ngModel]=\"field().placeholder || ''\"\n [ngModelOptions]=\"{standalone: true}\"\n (ngModelChange)=\"patch({ placeholder: $event })\">\n </ngs-form-field>\n }\n\n @if (!isSpacer()) {\n <ngs-form-field>\n <ngs-label>Hint</ngs-label>\n <input ngsInput\n [ngModel]=\"field().hint || ''\"\n [ngModelOptions]=\"{standalone: true}\"\n (ngModelChange)=\"patch({ hint: $event })\">\n </ngs-form-field>\n }\n\n <ngs-form-field>\n <ngs-label>Width</ngs-label>\n <ngs-select [ngModel]=\"field().width ?? 12\"\n [ngModelOptions]=\"{standalone: true}\"\n (ngModelChange)=\"patch({ width: $event })\">\n @for (width of fieldWidthOptions; track width) {\n <ngs-option [value]=\"width\">{{ width }}/12</ngs-option>\n }\n </ngs-select>\n </ngs-form-field>\n\n @if (hasBehaviorToggles()) {\n <ngs-slide-toggle-group class=\"mb-4\">\n <ngs-slide-toggle [ngModel]=\"field().required ?? false\"\n [ngModelOptions]=\"{standalone: true}\"\n (ngModelChange)=\"patch({ required: $event })\">\n Required field\n </ngs-slide-toggle>\n\n <ngs-slide-toggle [ngModel]=\"field().readonly ?? false\"\n [ngModelOptions]=\"{standalone: true}\"\n (ngModelChange)=\"patch({ readonly: $event })\">\n Readonly\n </ngs-slide-toggle>\n\n <ngs-slide-toggle [ngModel]=\"field().disabled ?? false\"\n [ngModelOptions]=\"{standalone: true}\"\n (ngModelChange)=\"patch({ disabled: $event })\">\n Disabled\n </ngs-slide-toggle>\n\n @if (hasCheckedState()) {\n <ngs-slide-toggle [ngModel]=\"field().defaultValue === true\"\n [ngModelOptions]=\"{standalone: true}\"\n (ngModelChange)=\"patch({ defaultValue: $event })\">\n Checked\n </ngs-slide-toggle>\n }\n\n @if (hasMultiple()) {\n <ngs-slide-toggle [ngModel]=\"field().multiple ?? false\"\n [ngModelOptions]=\"{standalone: true}\"\n (ngModelChange)=\"patchMultiple($event)\">\n Multiple\n </ngs-slide-toggle>\n }\n\n @if (hasClearable()) {\n <ngs-slide-toggle [ngModel]=\"field().clearable ?? false\"\n [ngModelOptions]=\"{standalone: true}\"\n (ngModelChange)=\"patch({ clearable: $event })\">\n Clearable\n </ngs-slide-toggle>\n }\n </ngs-slide-toggle-group>\n }\n\n @if (isUpload()) {\n <ngs-form-field>\n <ngs-label>Accepted file types</ngs-label>\n <input ngsInput\n [ngModel]=\"field().settings?.['accept'] || ''\"\n [ngModelOptions]=\"{standalone: true}\"\n (ngModelChange)=\"patchSettings({ accept: $event })\">\n <ngs-hint>Use MIME types separated by commas, for example image/*,application/pdf.</ngs-hint>\n </ngs-form-field>\n }\n\n @if (isSpacer()) {\n <ngs-form-field>\n <ngs-label>Height</ngs-label>\n <ngs-select [ngModel]=\"field().settings?.['height'] ?? 24\"\n [ngModelOptions]=\"{standalone: true}\"\n (ngModelChange)=\"patchSettings({ height: $event })\">\n @for (height of spacerHeightOptions; track height) {\n <ngs-option [value]=\"height\">{{ height }}px</ngs-option>\n }\n </ngs-select>\n </ngs-form-field>\n }\n\n @if (isRadio()) {\n <ngs-form-field>\n <ngs-label>Orientation</ngs-label>\n <ngs-select [ngModel]=\"radioOrientation()\"\n [ngModelOptions]=\"{standalone: true}\"\n (ngModelChange)=\"patchRadioOrientation($event)\">\n @for (orientation of radioOrientationOptions; track orientation) {\n <ngs-option [value]=\"orientation\">{{ orientation }}</ngs-option>\n }\n </ngs-select>\n </ngs-form-field>\n }\n\n @if (hasOptions()) {\n <ngs-form-field>\n <ngs-label>Options</ngs-label>\n <textarea ngsInput\n rows=\"4\"\n [formControl]=\"optionsControl\"></textarea>\n @if (optionsControl.hasError('formBuilderOptionsFormat')) {\n <ngs-error>Use Label:value or Label:value:selected.</ngs-error>\n }\n <ngs-hint>One option per line. Use Label:value or Label:value:selected.</ngs-hint>\n </ngs-form-field>\n }\n</div>\n", styles: [":host{display:block}:host .settings-section{display:flex;flex-direction:column;gap:calc(var(--spacing, .25rem) * 3);border-top:1px solid var(--ngs-color-outline-variant);padding-top:calc(var(--spacing, .25rem) * 4)}:host .settings-section h4{color:var(--ngs-color-on-surface);font-size:var(--ngs-font-size-sm);font-weight:600}\n/*! tailwindcss v4.2.2 | MIT License | https://tailwindcss.com */\n"], dependencies: [{ kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1.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: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }, { kind: "component", type: Error, selector: "ngs-error", exportAs: ["ngsError"] }, { kind: "component", type: FormField, selector: "ngs-form-field", inputs: ["subscriptHiddenIfEmpty", "sameHeightAsButton"], exportAs: ["ngsFormField"] }, { kind: "component", type: Hint, selector: "ngs-hint", inputs: ["align"], exportAs: ["ngsHint"] }, { kind: "component", type: Label, selector: "ngs-label" }, { kind: "directive", type: Input, selector: "input[ngsInput], textarea[ngsInput]", inputs: ["id", "placeholder", "required", "disabled", "readonly", "errorStateMatcher"], exportAs: ["ngsInput"] }, { kind: "component", type: Select, selector: "ngs-select", inputs: ["id", "placeholder", "disabled", "required", "multiple", "hideCheckIcon", "clearable", "aria-label", "tabIndex", "aria-describedby", "value"], outputs: ["selectionChange", "opened", "closed", "valueChange"], exportAs: ["ngsSelect"] }, { kind: "component", type: Option, selector: "ngs-option", inputs: ["value", "data", "disabled", "selected"], outputs: ["onSelectionChange"], exportAs: ["ngsOption"] }, { kind: "component", type: SlideToggle, selector: "ngs-slide-toggle", inputs: ["id", "name", "labelPosition", "aria-label", "aria-labelledby", "aria-describedby", "required", "disabled", "disableRipple", "tabIndex", "hideIcon", "color", "checked"], outputs: ["disabledChange", "checkedChange", "change", "toggleChange"], exportAs: ["ngsSlideToggle"] }, { kind: "component", type: SlideToggleGroup, selector: "ngs-slide-toggle-group", exportAs: ["ngsSlideToggleGroup"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
2907
|
+
}
|
|
2908
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: BasicFormBuilderFieldSettings, decorators: [{
|
|
2909
|
+
type: Component,
|
|
2910
|
+
args: [{ selector: 'ngs-basic-form-builder-field-settings', exportAs: 'ngsBasicFormBuilderFieldSettings', imports: [
|
|
2911
|
+
FormsModule,
|
|
2912
|
+
ReactiveFormsModule,
|
|
2913
|
+
Error,
|
|
2914
|
+
FormField,
|
|
2915
|
+
Hint,
|
|
2916
|
+
Label,
|
|
2917
|
+
Input,
|
|
2918
|
+
Select,
|
|
2919
|
+
Option,
|
|
2920
|
+
SlideToggle,
|
|
2921
|
+
SlideToggleGroup
|
|
2922
|
+
], changeDetection: ChangeDetectionStrategy.OnPush, host: {
|
|
2923
|
+
'class': 'ngs-basic-form-builder-field-settings'
|
|
2924
|
+
}, template: "<div class=\"flex flex-col gap-4\">\n <ngs-form-field>\n <ngs-label>Label</ngs-label>\n <input ngsInput\n [ngModel]=\"field().label\"\n [ngModelOptions]=\"{standalone: true}\"\n (ngModelChange)=\"patch({ label: $event })\">\n </ngs-form-field>\n\n @if (!isSpacer()) {\n <ngs-form-field>\n <ngs-label>Field ID</ngs-label>\n <input ngsInput\n [ngModel]=\"field().name\"\n [ngModelOptions]=\"{standalone: true}\"\n (ngModelChange)=\"patch({ name: $event })\">\n </ngs-form-field>\n }\n\n @if (hasPlaceholder()) {\n <ngs-form-field>\n <ngs-label>Placeholder</ngs-label>\n <input ngsInput\n [ngModel]=\"field().placeholder || ''\"\n [ngModelOptions]=\"{standalone: true}\"\n (ngModelChange)=\"patch({ placeholder: $event })\">\n </ngs-form-field>\n }\n\n @if (!isSpacer()) {\n <ngs-form-field>\n <ngs-label>Hint</ngs-label>\n <input ngsInput\n [ngModel]=\"field().hint || ''\"\n [ngModelOptions]=\"{standalone: true}\"\n (ngModelChange)=\"patch({ hint: $event })\">\n </ngs-form-field>\n }\n\n <ngs-form-field>\n <ngs-label>Width</ngs-label>\n <ngs-select [ngModel]=\"field().width ?? 12\"\n [ngModelOptions]=\"{standalone: true}\"\n (ngModelChange)=\"patch({ width: $event })\">\n @for (width of fieldWidthOptions; track width) {\n <ngs-option [value]=\"width\">{{ width }}/12</ngs-option>\n }\n </ngs-select>\n </ngs-form-field>\n\n @if (hasBehaviorToggles()) {\n <ngs-slide-toggle-group class=\"mb-4\">\n <ngs-slide-toggle [ngModel]=\"field().required ?? false\"\n [ngModelOptions]=\"{standalone: true}\"\n (ngModelChange)=\"patch({ required: $event })\">\n Required field\n </ngs-slide-toggle>\n\n <ngs-slide-toggle [ngModel]=\"field().readonly ?? false\"\n [ngModelOptions]=\"{standalone: true}\"\n (ngModelChange)=\"patch({ readonly: $event })\">\n Readonly\n </ngs-slide-toggle>\n\n <ngs-slide-toggle [ngModel]=\"field().disabled ?? false\"\n [ngModelOptions]=\"{standalone: true}\"\n (ngModelChange)=\"patch({ disabled: $event })\">\n Disabled\n </ngs-slide-toggle>\n\n @if (hasCheckedState()) {\n <ngs-slide-toggle [ngModel]=\"field().defaultValue === true\"\n [ngModelOptions]=\"{standalone: true}\"\n (ngModelChange)=\"patch({ defaultValue: $event })\">\n Checked\n </ngs-slide-toggle>\n }\n\n @if (hasMultiple()) {\n <ngs-slide-toggle [ngModel]=\"field().multiple ?? false\"\n [ngModelOptions]=\"{standalone: true}\"\n (ngModelChange)=\"patchMultiple($event)\">\n Multiple\n </ngs-slide-toggle>\n }\n\n @if (hasClearable()) {\n <ngs-slide-toggle [ngModel]=\"field().clearable ?? false\"\n [ngModelOptions]=\"{standalone: true}\"\n (ngModelChange)=\"patch({ clearable: $event })\">\n Clearable\n </ngs-slide-toggle>\n }\n </ngs-slide-toggle-group>\n }\n\n @if (isUpload()) {\n <ngs-form-field>\n <ngs-label>Accepted file types</ngs-label>\n <input ngsInput\n [ngModel]=\"field().settings?.['accept'] || ''\"\n [ngModelOptions]=\"{standalone: true}\"\n (ngModelChange)=\"patchSettings({ accept: $event })\">\n <ngs-hint>Use MIME types separated by commas, for example image/*,application/pdf.</ngs-hint>\n </ngs-form-field>\n }\n\n @if (isSpacer()) {\n <ngs-form-field>\n <ngs-label>Height</ngs-label>\n <ngs-select [ngModel]=\"field().settings?.['height'] ?? 24\"\n [ngModelOptions]=\"{standalone: true}\"\n (ngModelChange)=\"patchSettings({ height: $event })\">\n @for (height of spacerHeightOptions; track height) {\n <ngs-option [value]=\"height\">{{ height }}px</ngs-option>\n }\n </ngs-select>\n </ngs-form-field>\n }\n\n @if (isRadio()) {\n <ngs-form-field>\n <ngs-label>Orientation</ngs-label>\n <ngs-select [ngModel]=\"radioOrientation()\"\n [ngModelOptions]=\"{standalone: true}\"\n (ngModelChange)=\"patchRadioOrientation($event)\">\n @for (orientation of radioOrientationOptions; track orientation) {\n <ngs-option [value]=\"orientation\">{{ orientation }}</ngs-option>\n }\n </ngs-select>\n </ngs-form-field>\n }\n\n @if (hasOptions()) {\n <ngs-form-field>\n <ngs-label>Options</ngs-label>\n <textarea ngsInput\n rows=\"4\"\n [formControl]=\"optionsControl\"></textarea>\n @if (optionsControl.hasError('formBuilderOptionsFormat')) {\n <ngs-error>Use Label:value or Label:value:selected.</ngs-error>\n }\n <ngs-hint>One option per line. Use Label:value or Label:value:selected.</ngs-hint>\n </ngs-form-field>\n }\n</div>\n", styles: [":host{display:block}:host .settings-section{display:flex;flex-direction:column;gap:calc(var(--spacing, .25rem) * 3);border-top:1px solid var(--ngs-color-outline-variant);padding-top:calc(var(--spacing, .25rem) * 4)}:host .settings-section h4{color:var(--ngs-color-on-surface);font-size:var(--ngs-font-size-sm);font-weight:600}\n/*! tailwindcss v4.2.2 | MIT License | https://tailwindcss.com */\n"] }]
|
|
2925
|
+
}], ctorParameters: () => [], propDecorators: { field: [{ type: i0.Input, args: [{ isSignal: true, alias: "field", required: true }] }], update: [{ type: i0.Input, args: [{ isSignal: true, alias: "update", required: true }] }] } });
|
|
2926
|
+
function optionsTextValidator(control) {
|
|
2927
|
+
const hasInvalidLine = (control.value ?? '')
|
|
2928
|
+
.split('\n')
|
|
2929
|
+
.map(line => line.trim())
|
|
2930
|
+
.filter(Boolean)
|
|
2931
|
+
.some(line => !parseOptionLine(line));
|
|
2932
|
+
return hasInvalidLine ? { formBuilderOptionsFormat: true } : null;
|
|
2933
|
+
}
|
|
2934
|
+
function parseOptionLine(line) {
|
|
2935
|
+
const parts = line.split(':').map(part => part.trim());
|
|
2936
|
+
if (parts.length < 2 || parts.length > 3) {
|
|
2937
|
+
return null;
|
|
2938
|
+
}
|
|
2939
|
+
const [label, value, selected] = parts;
|
|
2940
|
+
if (!label || !value) {
|
|
2941
|
+
return null;
|
|
2942
|
+
}
|
|
2943
|
+
if (selected !== undefined && selected !== 'selected') {
|
|
2944
|
+
return null;
|
|
2945
|
+
}
|
|
2946
|
+
return {
|
|
2947
|
+
label,
|
|
2948
|
+
value,
|
|
2949
|
+
selected: selected === 'selected'
|
|
2950
|
+
};
|
|
2951
|
+
}
|
|
2952
|
+
|
|
2953
|
+
class BasicFormBuilderLayoutSettings {
|
|
2954
|
+
field = input.required(...(ngDevMode ? [{ debugName: "field" }] : /* istanbul ignore next */ []));
|
|
2955
|
+
update = input.required(...(ngDevMode ? [{ debugName: "update" }] : /* istanbul ignore next */ []));
|
|
2956
|
+
fieldWidthOptions = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12];
|
|
2957
|
+
showHintSetting = computed(() => this.field().type !== 'group', ...(ngDevMode ? [{ debugName: "showHintSetting" }] : /* istanbul ignore next */ []));
|
|
2958
|
+
patch(changes) {
|
|
2959
|
+
this.update()(changes);
|
|
2960
|
+
}
|
|
2961
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: BasicFormBuilderLayoutSettings, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
2962
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.4", type: BasicFormBuilderLayoutSettings, isStandalone: true, selector: "ngs-basic-form-builder-layout-settings", inputs: { field: { classPropertyName: "field", publicName: "field", isSignal: true, isRequired: true, transformFunction: null }, update: { classPropertyName: "update", publicName: "update", isSignal: true, isRequired: true, transformFunction: null } }, host: { classAttribute: "ngs-basic-form-builder-layout-settings" }, exportAs: ["ngsBasicFormBuilderLayoutSettings"], ngImport: i0, template: "<div class=\"flex flex-col gap-4\">\n <ngs-form-field>\n <ngs-label>Label</ngs-label>\n <input ngsInput\n [ngModel]=\"field().label\"\n [ngModelOptions]=\"{standalone: true}\"\n (ngModelChange)=\"patch({ label: $event })\">\n </ngs-form-field>\n\n @if (showHintSetting()) {\n <ngs-form-field>\n <ngs-label>Hint</ngs-label>\n <input ngsInput\n [ngModel]=\"field().hint || ''\"\n [ngModelOptions]=\"{standalone: true}\"\n (ngModelChange)=\"patch({ hint: $event })\">\n <ngs-hint>Shown below the layout title in preview and renderer.</ngs-hint>\n </ngs-form-field>\n }\n\n <ngs-form-field>\n <ngs-label>Width</ngs-label>\n <ngs-select [ngModel]=\"field().width ?? 12\"\n [ngModelOptions]=\"{standalone: true}\"\n (ngModelChange)=\"patch({ width: $event })\">\n @for (width of fieldWidthOptions; track width) {\n <ngs-option [value]=\"width\">{{ width }}/12</ngs-option>\n }\n </ngs-select>\n </ngs-form-field>\n</div>\n", styles: [":host{display:block}\n/*! tailwindcss v4.2.2 | MIT License | https://tailwindcss.com */\n"], dependencies: [{ kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1.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: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "component", type: FormField, selector: "ngs-form-field", inputs: ["subscriptHiddenIfEmpty", "sameHeightAsButton"], exportAs: ["ngsFormField"] }, { kind: "component", type: Hint, selector: "ngs-hint", inputs: ["align"], exportAs: ["ngsHint"] }, { kind: "component", type: Label, selector: "ngs-label" }, { kind: "directive", type: Input, selector: "input[ngsInput], textarea[ngsInput]", inputs: ["id", "placeholder", "required", "disabled", "readonly", "errorStateMatcher"], exportAs: ["ngsInput"] }, { kind: "component", type: Option, selector: "ngs-option", inputs: ["value", "data", "disabled", "selected"], outputs: ["onSelectionChange"], exportAs: ["ngsOption"] }, { kind: "component", type: Select, selector: "ngs-select", inputs: ["id", "placeholder", "disabled", "required", "multiple", "hideCheckIcon", "clearable", "aria-label", "tabIndex", "aria-describedby", "value"], outputs: ["selectionChange", "opened", "closed", "valueChange"], exportAs: ["ngsSelect"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
2963
|
+
}
|
|
2964
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: BasicFormBuilderLayoutSettings, decorators: [{
|
|
2965
|
+
type: Component,
|
|
2966
|
+
args: [{ selector: 'ngs-basic-form-builder-layout-settings', exportAs: 'ngsBasicFormBuilderLayoutSettings', imports: [
|
|
2967
|
+
FormsModule,
|
|
2968
|
+
FormField,
|
|
2969
|
+
Hint,
|
|
2970
|
+
Label,
|
|
2971
|
+
Input,
|
|
2972
|
+
Option,
|
|
2973
|
+
Select
|
|
2974
|
+
], changeDetection: ChangeDetectionStrategy.OnPush, host: {
|
|
2975
|
+
'class': 'ngs-basic-form-builder-layout-settings'
|
|
2976
|
+
}, template: "<div class=\"flex flex-col gap-4\">\n <ngs-form-field>\n <ngs-label>Label</ngs-label>\n <input ngsInput\n [ngModel]=\"field().label\"\n [ngModelOptions]=\"{standalone: true}\"\n (ngModelChange)=\"patch({ label: $event })\">\n </ngs-form-field>\n\n @if (showHintSetting()) {\n <ngs-form-field>\n <ngs-label>Hint</ngs-label>\n <input ngsInput\n [ngModel]=\"field().hint || ''\"\n [ngModelOptions]=\"{standalone: true}\"\n (ngModelChange)=\"patch({ hint: $event })\">\n <ngs-hint>Shown below the layout title in preview and renderer.</ngs-hint>\n </ngs-form-field>\n }\n\n <ngs-form-field>\n <ngs-label>Width</ngs-label>\n <ngs-select [ngModel]=\"field().width ?? 12\"\n [ngModelOptions]=\"{standalone: true}\"\n (ngModelChange)=\"patch({ width: $event })\">\n @for (width of fieldWidthOptions; track width) {\n <ngs-option [value]=\"width\">{{ width }}/12</ngs-option>\n }\n </ngs-select>\n </ngs-form-field>\n</div>\n", styles: [":host{display:block}\n/*! tailwindcss v4.2.2 | MIT License | https://tailwindcss.com */\n"] }]
|
|
2977
|
+
}], propDecorators: { field: [{ type: i0.Input, args: [{ isSignal: true, alias: "field", required: true }] }], update: [{ type: i0.Input, args: [{ isSignal: true, alias: "update", required: true }] }] } });
|
|
2978
|
+
|
|
2979
|
+
class BasicFormBuilderSectionSettings {
|
|
2980
|
+
section = input.required(...(ngDevMode ? [{ debugName: "section" }] : /* istanbul ignore next */ []));
|
|
2981
|
+
update = input.required(...(ngDevMode ? [{ debugName: "update" }] : /* istanbul ignore next */ []));
|
|
2982
|
+
patch(changes) {
|
|
2983
|
+
this.update()(changes);
|
|
2984
|
+
}
|
|
2985
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: BasicFormBuilderSectionSettings, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
2986
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "21.2.4", type: BasicFormBuilderSectionSettings, isStandalone: true, selector: "ngs-basic-form-builder-section-settings", inputs: { section: { classPropertyName: "section", publicName: "section", isSignal: true, isRequired: true, transformFunction: null }, update: { classPropertyName: "update", publicName: "update", isSignal: true, isRequired: true, transformFunction: null } }, host: { classAttribute: "ngs-basic-form-builder-section-settings" }, exportAs: ["ngsBasicFormBuilderSectionSettings"], ngImport: i0, template: "<div class=\"flex flex-col gap-4\">\n <ngs-form-field>\n <ngs-label>Title</ngs-label>\n <input ngsInput\n [ngModel]=\"section().title\"\n [ngModelOptions]=\"{standalone: true}\"\n (ngModelChange)=\"patch({ title: $event })\">\n </ngs-form-field>\n\n <ngs-form-field>\n <ngs-label>Description</ngs-label>\n <textarea ngsInput\n rows=\"3\"\n [ngModel]=\"section().description || ''\"\n [ngModelOptions]=\"{standalone: true}\"\n (ngModelChange)=\"patch({ description: $event })\"></textarea>\n <ngs-hint>Optional helper text rendered under the section title.</ngs-hint>\n </ngs-form-field>\n\n <ngs-slide-toggle [ngModel]=\"section().collapsed ?? false\"\n [ngModelOptions]=\"{standalone: true}\"\n (ngModelChange)=\"patch({ collapsed: $event })\">\n Collapsed\n </ngs-slide-toggle>\n</div>\n", styles: [":host{display:block}\n/*! tailwindcss v4.2.2 | MIT License | https://tailwindcss.com */\n"], dependencies: [{ kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1.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: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "component", type: FormField, selector: "ngs-form-field", inputs: ["subscriptHiddenIfEmpty", "sameHeightAsButton"], exportAs: ["ngsFormField"] }, { kind: "component", type: Hint, selector: "ngs-hint", inputs: ["align"], exportAs: ["ngsHint"] }, { kind: "component", type: Label, selector: "ngs-label" }, { kind: "directive", type: Input, selector: "input[ngsInput], textarea[ngsInput]", inputs: ["id", "placeholder", "required", "disabled", "readonly", "errorStateMatcher"], exportAs: ["ngsInput"] }, { kind: "component", type: SlideToggle, selector: "ngs-slide-toggle", inputs: ["id", "name", "labelPosition", "aria-label", "aria-labelledby", "aria-describedby", "required", "disabled", "disableRipple", "tabIndex", "hideIcon", "color", "checked"], outputs: ["disabledChange", "checkedChange", "change", "toggleChange"], exportAs: ["ngsSlideToggle"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
2987
|
+
}
|
|
2988
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: BasicFormBuilderSectionSettings, decorators: [{
|
|
2989
|
+
type: Component,
|
|
2990
|
+
args: [{ selector: 'ngs-basic-form-builder-section-settings', exportAs: 'ngsBasicFormBuilderSectionSettings', imports: [
|
|
2991
|
+
FormsModule,
|
|
2992
|
+
FormField,
|
|
2993
|
+
Hint,
|
|
2994
|
+
Label,
|
|
2995
|
+
Input,
|
|
2996
|
+
SlideToggle
|
|
2997
|
+
], changeDetection: ChangeDetectionStrategy.OnPush, host: {
|
|
2998
|
+
'class': 'ngs-basic-form-builder-section-settings'
|
|
2999
|
+
}, template: "<div class=\"flex flex-col gap-4\">\n <ngs-form-field>\n <ngs-label>Title</ngs-label>\n <input ngsInput\n [ngModel]=\"section().title\"\n [ngModelOptions]=\"{standalone: true}\"\n (ngModelChange)=\"patch({ title: $event })\">\n </ngs-form-field>\n\n <ngs-form-field>\n <ngs-label>Description</ngs-label>\n <textarea ngsInput\n rows=\"3\"\n [ngModel]=\"section().description || ''\"\n [ngModelOptions]=\"{standalone: true}\"\n (ngModelChange)=\"patch({ description: $event })\"></textarea>\n <ngs-hint>Optional helper text rendered under the section title.</ngs-hint>\n </ngs-form-field>\n\n <ngs-slide-toggle [ngModel]=\"section().collapsed ?? false\"\n [ngModelOptions]=\"{standalone: true}\"\n (ngModelChange)=\"patch({ collapsed: $event })\">\n Collapsed\n </ngs-slide-toggle>\n</div>\n", styles: [":host{display:block}\n/*! tailwindcss v4.2.2 | MIT License | https://tailwindcss.com */\n"] }]
|
|
3000
|
+
}], propDecorators: { section: [{ type: i0.Input, args: [{ isSignal: true, alias: "section", required: true }] }], update: [{ type: i0.Input, args: [{ isSignal: true, alias: "update", required: true }] }] } });
|
|
3001
|
+
|
|
2309
3002
|
/**
|
|
2310
3003
|
* Generated bundle index. Do not edit.
|
|
2311
3004
|
*/
|
|
2312
3005
|
|
|
2313
|
-
export { BasicFormBuilderFieldSettings, BasicFormBuilderLayoutSettings, BasicFormBuilderSectionSettings, DEFAULT_FORM_BUILDER_FIELDS, DEFAULT_FORM_BUILDER_ITEMS, FORM_BUILDER_FIELDS, FORM_BUILDER_ITEMS, FORM_BUILDER_SETTINGS, FORM_BUILDER_UPLOAD_CALLBACK, FormBuilder, FormBuilderFieldHost, FormBuilderRenderer, FormBuilderSettingsHost, createDefaultFormBuilderSchema, formBuilderField, formBuilderItem, formBuilderSettings, provideFormBuilder, validatorsFromRules };
|
|
3006
|
+
export { BasicFormBuilderFieldSettings, BasicFormBuilderLayoutSettings, BasicFormBuilderSectionSettings, DEFAULT_FORM_BUILDER_FIELDS, DEFAULT_FORM_BUILDER_ITEMS, FORM_BUILDER_FIELDS, FORM_BUILDER_FIELD_BASE_SETTINGS_SCHEMA, FORM_BUILDER_INPUT_FIELD_BASE_SETTINGS_SCHEMA, FORM_BUILDER_ITEMS, FORM_BUILDER_LAYOUT_BASE_SETTINGS_SCHEMA, FORM_BUILDER_LAYOUT_CONTAINER_BASE_SETTINGS_SCHEMA, FORM_BUILDER_SECTION_BASE_SETTINGS_SCHEMA, FORM_BUILDER_SETTINGS, FORM_BUILDER_STATIC_BASE_SETTINGS_SCHEMA, FORM_BUILDER_UPLOAD_CALLBACK, FormBuilder, FormBuilderFieldHost, FormBuilderRenderer, FormBuilderSettingsHost, createDefaultFormBuilderSchema, formBuilderField, formBuilderItem, formBuilderSettings, provideFormBuilder, provideFormBuilderField, provideFormBuilderFields, validatorsFromRules };
|
|
2314
3007
|
//# sourceMappingURL=ngstarter-ui-components-form-builder.mjs.map
|