@ngstarter-ui/components 21.0.46 → 21.0.48

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.
@@ -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, DestroyRef, ElementRef } from '@angular/core';
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,144 @@ 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
+ };
201
+ const HIDDEN_SETTINGS_SCHEMA = {
202
+ sections: [
203
+ {
204
+ id: 'hidden-settings',
205
+ title: 'Hidden field',
206
+ fields: [
207
+ { id: 'hidden-label', name: 'label', type: 'text', label: 'Label' },
208
+ { id: 'hidden-name', name: 'name', type: 'text', label: 'Field ID' },
209
+ { id: 'hidden-default-value', name: 'defaultValue', type: 'text', label: 'Value' },
210
+ { id: 'hidden-disabled', name: 'disabled', type: 'toggle', label: 'Disabled', defaultValue: false }
211
+ ]
212
+ }
213
+ ]
214
+ };
67
215
  const DEFAULT_FORM_BUILDER_FIELDS = [
68
216
  {
69
217
  type: 'text',
@@ -73,6 +221,9 @@ const DEFAULT_FORM_BUILDER_FIELDS = [
73
221
  defaults: {
74
222
  label: 'Text field',
75
223
  placeholder: 'Enter text'
224
+ },
225
+ settings: {
226
+ extends: 'input-field'
76
227
  }
77
228
  },
78
229
  {
@@ -83,6 +234,9 @@ const DEFAULT_FORM_BUILDER_FIELDS = [
83
234
  defaults: {
84
235
  label: 'Number',
85
236
  placeholder: '0'
237
+ },
238
+ settings: {
239
+ extends: 'input-field'
86
240
  }
87
241
  },
88
242
  {
@@ -94,8 +248,26 @@ const DEFAULT_FORM_BUILDER_FIELDS = [
94
248
  label: 'Email',
95
249
  placeholder: 'name@example.com'
96
250
  },
251
+ settings: {
252
+ extends: 'input-field'
253
+ },
97
254
  validators: field => field.required ? [Validators.required, Validators.email] : [Validators.email]
98
255
  },
256
+ {
257
+ type: 'hidden',
258
+ label: 'Hidden',
259
+ group: 'Basic',
260
+ icon: 'fluent:eye-off-24-regular',
261
+ description: 'Native hidden input included in submitted form values.',
262
+ defaults: {
263
+ label: 'Hidden field',
264
+ defaultValue: ''
265
+ },
266
+ settings: {
267
+ extends: 'none',
268
+ schema: HIDDEN_SETTINGS_SCHEMA
269
+ }
270
+ },
99
271
  {
100
272
  type: 'textarea',
101
273
  label: 'Textarea',
@@ -105,6 +277,9 @@ const DEFAULT_FORM_BUILDER_FIELDS = [
105
277
  label: 'Description',
106
278
  placeholder: 'Enter description',
107
279
  width: 12
280
+ },
281
+ settings: {
282
+ extends: 'input-field'
108
283
  }
109
284
  },
110
285
  {
@@ -120,6 +295,56 @@ const DEFAULT_FORM_BUILDER_FIELDS = [
120
295
  label: 'Group',
121
296
  width: 12,
122
297
  children: []
298
+ },
299
+ settings: {
300
+ extends: 'layout-container'
301
+ }
302
+ },
303
+ {
304
+ type: 'repeater',
305
+ label: 'Repeater',
306
+ kind: 'layout',
307
+ group: 'Layout',
308
+ icon: 'fluent:copy-add-24-regular',
309
+ description: 'Repeat a group of nested fields as a FormArray.',
310
+ acceptsChildren: true,
311
+ defaults: {
312
+ kind: 'layout',
313
+ label: 'Repeater',
314
+ name: 'items',
315
+ width: 12,
316
+ settings: {
317
+ allowNullValue: false,
318
+ emptyText: 'No items added yet.'
319
+ },
320
+ children: []
321
+ },
322
+ settings: {
323
+ extends: 'layout-container',
324
+ schema: {
325
+ sections: [
326
+ {
327
+ id: 'repeater-settings',
328
+ title: 'Repeater',
329
+ fields: [
330
+ {
331
+ id: 'repeater-allow-null-value',
332
+ name: 'settings.allowNullValue',
333
+ type: 'toggle',
334
+ label: 'Allow null value',
335
+ defaultValue: false
336
+ },
337
+ {
338
+ id: 'repeater-empty-text',
339
+ name: 'settings.emptyText',
340
+ type: 'textarea',
341
+ label: 'Empty text',
342
+ defaultValue: 'No items added yet.'
343
+ }
344
+ ]
345
+ }
346
+ ]
347
+ }
123
348
  }
124
349
  },
125
350
  {
@@ -136,6 +361,20 @@ const DEFAULT_FORM_BUILDER_FIELDS = [
136
361
  settings: {
137
362
  height: 24
138
363
  }
364
+ },
365
+ settings: {
366
+ extends: 'static',
367
+ schema: {
368
+ sections: [
369
+ {
370
+ id: 'spacer-settings',
371
+ title: 'Spacer',
372
+ fields: [
373
+ { id: 'spacer-height', name: 'settings.height', type: 'select', label: 'Height', defaultValue: 24, options: SPACER_HEIGHT_OPTIONS }
374
+ ]
375
+ }
376
+ ]
377
+ }
139
378
  }
140
379
  },
141
380
  {
@@ -151,6 +390,31 @@ const DEFAULT_FORM_BUILDER_FIELDS = [
151
390
  { label: 'Option 1', value: 'option_1' },
152
391
  { label: 'Option 2', value: 'option_2' }
153
392
  ]
393
+ },
394
+ settings: {
395
+ extends: 'input-field',
396
+ schema: {
397
+ sections: [
398
+ {
399
+ id: 'select-behavior-settings',
400
+ title: 'Select',
401
+ fields: [
402
+ {
403
+ id: 'select-multiple',
404
+ name: 'multiple',
405
+ type: 'toggle',
406
+ label: 'Multiple',
407
+ defaultValue: false,
408
+ settings: {
409
+ valueAdapter: 'selectMultiple'
410
+ }
411
+ },
412
+ { id: 'select-clearable', name: 'clearable', type: 'toggle', label: 'Clearable', defaultValue: true }
413
+ ]
414
+ },
415
+ ...OPTIONS_SETTINGS_SCHEMA.sections
416
+ ]
417
+ }
154
418
  }
155
419
  },
156
420
  {
@@ -167,6 +431,21 @@ const DEFAULT_FORM_BUILDER_FIELDS = [
167
431
  { label: 'Option 1', value: 'option_1' },
168
432
  { label: 'Option 2', value: 'option_2' }
169
433
  ]
434
+ },
435
+ settings: {
436
+ extends: 'field',
437
+ schema: {
438
+ sections: [
439
+ {
440
+ id: 'radio-settings',
441
+ title: 'Radio',
442
+ fields: [
443
+ { id: 'radio-orientation', name: 'settings.orientation', type: 'select', label: 'Orientation', defaultValue: 'vertical', options: ORIENTATION_OPTIONS }
444
+ ]
445
+ },
446
+ ...OPTIONS_SETTINGS_SCHEMA.sections
447
+ ]
448
+ }
170
449
  }
171
450
  },
172
451
  {
@@ -177,6 +456,10 @@ const DEFAULT_FORM_BUILDER_FIELDS = [
177
456
  defaults: {
178
457
  label: 'Checkbox',
179
458
  defaultValue: false
459
+ },
460
+ settings: {
461
+ extends: 'field',
462
+ schema: CHECKED_SETTINGS_SCHEMA
180
463
  }
181
464
  },
182
465
  {
@@ -187,6 +470,10 @@ const DEFAULT_FORM_BUILDER_FIELDS = [
187
470
  defaults: {
188
471
  label: 'Toggle',
189
472
  defaultValue: false
473
+ },
474
+ settings: {
475
+ extends: 'field',
476
+ schema: CHECKED_SETTINGS_SCHEMA
190
477
  }
191
478
  },
192
479
  {
@@ -197,6 +484,9 @@ const DEFAULT_FORM_BUILDER_FIELDS = [
197
484
  defaults: {
198
485
  label: 'Date',
199
486
  placeholder: 'Select date'
487
+ },
488
+ settings: {
489
+ extends: 'input-field'
200
490
  }
201
491
  },
202
492
  {
@@ -207,6 +497,9 @@ const DEFAULT_FORM_BUILDER_FIELDS = [
207
497
  defaults: {
208
498
  label: 'Time',
209
499
  placeholder: 'Select time'
500
+ },
501
+ settings: {
502
+ extends: 'input-field'
210
503
  }
211
504
  },
212
505
  {
@@ -218,6 +511,9 @@ const DEFAULT_FORM_BUILDER_FIELDS = [
218
511
  label: 'Date range',
219
512
  placeholder: 'Start date',
220
513
  width: 12
514
+ },
515
+ settings: {
516
+ extends: 'input-field'
221
517
  }
222
518
  },
223
519
  {
@@ -233,6 +529,37 @@ const DEFAULT_FORM_BUILDER_FIELDS = [
233
529
  settings: {
234
530
  accept: '*/*'
235
531
  }
532
+ },
533
+ settings: {
534
+ extends: 'input-field',
535
+ schema: {
536
+ sections: [
537
+ {
538
+ id: 'upload-settings',
539
+ title: 'Upload',
540
+ fields: [
541
+ {
542
+ id: 'upload-multiple',
543
+ name: 'multiple',
544
+ type: 'toggle',
545
+ label: 'Multiple',
546
+ defaultValue: false,
547
+ settings: {
548
+ valueAdapter: 'multipleDefaultValue'
549
+ }
550
+ },
551
+ {
552
+ id: 'upload-accept',
553
+ name: 'settings.accept',
554
+ type: 'text',
555
+ label: 'Accepted file types',
556
+ defaultValue: '*/*',
557
+ hint: 'Use MIME types separated by commas, for example image/*,application/pdf.'
558
+ }
559
+ ]
560
+ }
561
+ ]
562
+ }
236
563
  }
237
564
  },
238
565
  {
@@ -243,6 +570,9 @@ const DEFAULT_FORM_BUILDER_FIELDS = [
243
570
  defaults: {
244
571
  label: 'Timezone',
245
572
  placeholder: 'Select timezone'
573
+ },
574
+ settings: {
575
+ extends: 'input-field'
246
576
  }
247
577
  },
248
578
  {
@@ -253,6 +583,9 @@ const DEFAULT_FORM_BUILDER_FIELDS = [
253
583
  defaults: {
254
584
  label: 'Amount',
255
585
  placeholder: '0.00'
586
+ },
587
+ settings: {
588
+ extends: 'input-field'
256
589
  }
257
590
  },
258
591
  {
@@ -263,6 +596,9 @@ const DEFAULT_FORM_BUILDER_FIELDS = [
263
596
  defaults: {
264
597
  label: 'Currency',
265
598
  placeholder: 'Select currency'
599
+ },
600
+ settings: {
601
+ extends: 'input-field'
266
602
  }
267
603
  },
268
604
  {
@@ -274,6 +610,20 @@ const DEFAULT_FORM_BUILDER_FIELDS = [
274
610
  label: 'Country',
275
611
  placeholder: 'Select country',
276
612
  clearable: true
613
+ },
614
+ settings: {
615
+ extends: 'input-field',
616
+ schema: {
617
+ sections: [
618
+ {
619
+ id: 'country-select-settings',
620
+ title: 'Country select',
621
+ fields: [
622
+ { id: 'country-clearable', name: 'clearable', type: 'toggle', label: 'Clearable', defaultValue: true }
623
+ ]
624
+ }
625
+ ]
626
+ }
277
627
  }
278
628
  }
279
629
  ];
@@ -285,7 +635,11 @@ const DEFAULT_FORM_BUILDER_ITEMS = [
285
635
  group: 'Layout',
286
636
  icon: 'fluent:folder-24-regular',
287
637
  description: 'Top-level layout container.',
288
- acceptsChildren: true
638
+ acceptsChildren: true,
639
+ settings: {
640
+ extends: 'none',
641
+ schema: FORM_BUILDER_SECTION_BASE_SETTINGS_SCHEMA
642
+ }
289
643
  },
290
644
  ...DEFAULT_FORM_BUILDER_FIELDS
291
645
  ];
@@ -430,9 +784,9 @@ class FormBuilderFieldHost {
430
784
  return String(value);
431
785
  }
432
786
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: FormBuilderFieldHost, deps: [], target: i0.ɵɵFactoryTarget.Component });
433
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.4", type: FormBuilderFieldHost, isStandalone: true, selector: "ngs-form-builder-field-host", inputs: { field: { classPropertyName: "field", publicName: "field", isSignal: true, isRequired: true, transformFunction: null }, control: { classPropertyName: "control", publicName: "control", isSignal: true, isRequired: true, transformFunction: null }, definitions: { classPropertyName: "definitions", publicName: "definitions", isSignal: true, isRequired: false, transformFunction: null }, readonly: { classPropertyName: "readonly", publicName: "readonly", isSignal: true, isRequired: false, transformFunction: null }, editableCanvas: { classPropertyName: "editableCanvas", publicName: "editableCanvas", isSignal: true, isRequired: false, transformFunction: null }, uploadCallback: { classPropertyName: "uploadCallback", publicName: "uploadCallback", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "class.is-custom": "customLoaded()", "class.is-width-1": "!editableCanvas() && field().width === 1", "class.is-width-2": "!editableCanvas() && field().width === 2", "class.is-width-3": "!editableCanvas() && field().width === 3", "class.is-width-4": "!editableCanvas() && field().width === 4", "class.is-width-5": "!editableCanvas() && field().width === 5", "class.is-width-6": "!editableCanvas() && field().width === 6", "class.is-width-7": "!editableCanvas() && field().width === 7", "class.is-width-8": "!editableCanvas() && field().width === 8", "class.is-width-9": "!editableCanvas() && field().width === 9", "class.is-width-10": "!editableCanvas() && field().width === 10", "class.is-width-11": "!editableCanvas() && field().width === 11", "class.is-width-12": "!editableCanvas() && (field().width ?? 12) === 12" }, classAttribute: "ngs-form-builder-field-host" }, providers: [
787
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.4", type: FormBuilderFieldHost, isStandalone: true, selector: "ngs-form-builder-field-host", inputs: { field: { classPropertyName: "field", publicName: "field", isSignal: true, isRequired: true, transformFunction: null }, control: { classPropertyName: "control", publicName: "control", isSignal: true, isRequired: true, transformFunction: null }, definitions: { classPropertyName: "definitions", publicName: "definitions", isSignal: true, isRequired: false, transformFunction: null }, readonly: { classPropertyName: "readonly", publicName: "readonly", isSignal: true, isRequired: false, transformFunction: null }, editableCanvas: { classPropertyName: "editableCanvas", publicName: "editableCanvas", isSignal: true, isRequired: false, transformFunction: null }, uploadCallback: { classPropertyName: "uploadCallback", publicName: "uploadCallback", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "class.is-custom": "customLoaded()", "class.is-width-1": "!editableCanvas() && field().width === 1", "class.is-width-2": "!editableCanvas() && field().width === 2", "class.is-width-3": "!editableCanvas() && field().width === 3", "class.is-width-4": "!editableCanvas() && field().width === 4", "class.is-width-5": "!editableCanvas() && field().width === 5", "class.is-width-6": "!editableCanvas() && field().width === 6", "class.is-width-7": "!editableCanvas() && field().width === 7", "class.is-width-8": "!editableCanvas() && field().width === 8", "class.is-width-9": "!editableCanvas() && field().width === 9", "class.is-width-10": "!editableCanvas() && field().width === 10", "class.is-width-11": "!editableCanvas() && field().width === 11", "class.is-width-12": "!editableCanvas() && (field().width ?? 12) === 12", "class.is-hidden-field": "!editableCanvas() && field().type === \"hidden\"" }, classAttribute: "ngs-form-builder-field-host" }, providers: [
434
788
  provideNativeDateAdapter()
435
- ], viewQueries: [{ propertyName: "anchor", first: true, predicate: ["anchor"], descendants: true, read: ViewContainerRef, isSignal: true }], exportAs: ["ngsFormBuilderFieldHost"], ngImport: i0, template: "<ng-container #anchor/>\n\n@if (!customLoaded()) {\n @switch (field().type) {\n @case ('textarea') {\n <ngs-form-field>\n <ngs-label>{{ field().label }}</ngs-label>\n <textarea ngsInput\n rows=\"3\"\n [formControl]=\"control()\"\n [placeholder]=\"field().placeholder || ''\"\n [readonly]=\"readonly() || field().readonly\"></textarea>\n @if (field().hint) {\n <ngs-hint>{{ field().hint }}</ngs-hint>\n }\n </ngs-form-field>\n }\n\n @case ('select') {\n <ngs-form-field>\n <ngs-label>{{ field().label }}</ngs-label>\n <ngs-select [formControl]=\"control()\"\n [placeholder]=\"field().placeholder || ''\"\n [multiple]=\"field().multiple ?? false\"\n [clearable]=\"field().clearable ?? false\">\n @for (option of field().options ?? []; track option.value) {\n <ngs-option [value]=\"option.value\">{{ option.label }}</ngs-option>\n }\n </ngs-select>\n @if (field().hint) {\n <ngs-hint>{{ field().hint }}</ngs-hint>\n }\n </ngs-form-field>\n }\n\n @case ('radio') {\n <div class=\"ngs-form-builder-choice-field\">\n <span class=\"ngs-form-builder-choice-label\">{{ field().label }}</span>\n <ngs-radio-group [formControl]=\"control()\"\n [name]=\"field().name\"\n [orientation]=\"radioOrientation()\">\n @for (option of field().options ?? []; track option.value) {\n <ngs-radio-button [value]=\"option.value\">{{ option.label }}</ngs-radio-button>\n }\n </ngs-radio-group>\n @if (field().hint) {\n <span class=\"ngs-form-builder-choice-hint\">{{ field().hint }}</span>\n }\n </div>\n }\n\n @case ('checkbox') {\n <div class=\"ngs-form-builder-check-field\">\n <ngs-checkbox [formControl]=\"control()\">\n {{ field().label }}\n </ngs-checkbox>\n @if (field().hint) {\n <span>{{ field().hint }}</span>\n }\n </div>\n }\n\n @case ('toggle') {\n <div class=\"ngs-form-builder-check-field\">\n <ngs-slide-toggle [formControl]=\"control()\">\n {{ field().label }}\n </ngs-slide-toggle>\n @if (field().hint) {\n <span>{{ field().hint }}</span>\n }\n </div>\n }\n\n @case ('date') {\n <ngs-form-field>\n <ngs-label>{{ field().label }}</ngs-label>\n <input ngsInput\n [ngsDatepicker]=\"picker\"\n [formControl]=\"control()\"\n [placeholder]=\"field().placeholder || ''\"\n [readonly]=\"readonly() || field().readonly\">\n <ngs-datepicker-toggle ngsIconButtonSuffix [for]=\"picker\"/>\n <ngs-datepicker #picker/>\n @if (field().hint) {\n <ngs-hint>{{ field().hint }}</ngs-hint>\n }\n </ngs-form-field>\n }\n\n @case ('time') {\n <ngs-form-field>\n <ngs-label>{{ field().label }}</ngs-label>\n <input ngsInput\n [ngsTimepicker]=\"timePicker\"\n [formControl]=\"control()\"\n [placeholder]=\"field().placeholder || ''\"\n [readonly]=\"readonly() || field().readonly\">\n <ngs-timepicker-toggle ngsIconButtonSuffix [for]=\"timePicker\"/>\n <ngs-timepicker #timePicker/>\n @if (field().hint) {\n <ngs-hint>{{ field().hint }}</ngs-hint>\n }\n </ngs-form-field>\n }\n\n @case ('date-range') {\n <ngs-form-field>\n <ngs-label>{{ field().label }}</ngs-label>\n <ngs-date-range-input #rangeInput [rangePicker]=\"rangePicker\">\n <input ngsStartDate\n [value]=\"dateRangeStartValue()\"\n [placeholder]=\"field().placeholder || 'Start date'\"\n [readonly]=\"readonly() || field().readonly\"\n [disabled]=\"dateRangeDisabled()\"\n (input)=\"onDateRangeChanged(rangeInput)\">\n <input ngsEndDate\n [value]=\"dateRangeEndValue()\"\n placeholder=\"End date\"\n [readonly]=\"readonly() || field().readonly\"\n [disabled]=\"dateRangeDisabled()\"\n (input)=\"onDateRangeChanged(rangeInput)\">\n </ngs-date-range-input>\n <ngs-datepicker-toggle ngsIconButtonSuffix [for]=\"rangePicker\"/>\n <ngs-date-range-picker #rangePicker/>\n @if (field().hint) {\n <ngs-hint>{{ field().hint }}</ngs-hint>\n }\n </ngs-form-field>\n }\n\n @case ('upload') {\n <div class=\"ngs-form-builder-upload-field\"\n [class.is-disabled]=\"uploadDisabled()\">\n <span class=\"ngs-form-builder-choice-label\">{{ field().label }}</span>\n <ngs-upload-area ngsUploadTrigger\n [accept]=\"uploadAccept()\"\n [multiple]=\"field().multiple ?? false\"\n (fileSelected)=\"onUploadFilesSelected($event)\">\n <ngs-icon name=\"fluent:arrow-upload-24-regular\" ngsUploadAreaIcon/>\n <ng-container ngsUploadAreaMainState>\n {{ uploadSelectedText() }}\n </ng-container>\n <ng-container ngsUploadAreaDropState>\n Drop files here\n </ng-container>\n <ng-container ngsUploadAreaInvalidState>\n Invalid file type\n </ng-container>\n </ngs-upload-area>\n @if (field().hint) {\n <span class=\"ngs-form-builder-choice-hint\">{{ field().hint }}</span>\n }\n </div>\n }\n\n @case ('spacer') {\n <div class=\"ngs-form-builder-spacer\"\n [class.is-height-8]=\"spacerHeight() === 8\"\n [class.is-height-16]=\"spacerHeight() === 16\"\n [class.is-height-24]=\"spacerHeight() === 24\"\n [class.is-height-32]=\"spacerHeight() === 32\"\n [class.is-height-48]=\"spacerHeight() === 48\"\n [class.is-height-64]=\"spacerHeight() === 64\"\n aria-hidden=\"true\"></div>\n }\n\n @case ('timezone-select') {\n <ngs-form-field>\n <ngs-label>{{ field().label }}</ngs-label>\n <ngs-timezone-select [formControl]=\"control()\"\n [placeholder]=\"field().placeholder || ''\"\n [required]=\"field().required ?? false\"/>\n @if (field().hint) {\n <ngs-hint>{{ field().hint }}</ngs-hint>\n }\n </ngs-form-field>\n }\n\n @case ('currency') {\n <ngs-form-field>\n <ngs-label>{{ field().label }}</ngs-label>\n <input ngsInput\n type=\"number\"\n inputmode=\"decimal\"\n [formControl]=\"control()\"\n [placeholder]=\"field().placeholder || ''\"\n [readonly]=\"readonly() || field().readonly\">\n @if (field().hint) {\n <ngs-hint>{{ field().hint }}</ngs-hint>\n }\n </ngs-form-field>\n }\n\n @case ('currency-select') {\n <ngs-form-field>\n <ngs-label>{{ field().label }}</ngs-label>\n <ngs-currency-select [formControl]=\"control()\"\n [placeholder]=\"field().placeholder || ''\"\n [required]=\"field().required ?? false\"/>\n @if (field().hint) {\n <ngs-hint>{{ field().hint }}</ngs-hint>\n }\n </ngs-form-field>\n }\n\n @case ('country-select') {\n <ngs-form-field>\n <ngs-label>{{ field().label }}</ngs-label>\n <ngs-country-select [formControl]=\"control()\"\n [placeholder]=\"field().placeholder || ''\"\n [clearable]=\"field().clearable ?? false\"\n [required]=\"field().required ?? false\"/>\n @if (field().hint) {\n <ngs-hint>{{ field().hint }}</ngs-hint>\n }\n </ngs-form-field>\n }\n\n @default {\n <ngs-form-field>\n <ngs-label>{{ field().label }}</ngs-label>\n <input ngsInput\n [type]=\"textInputType()\"\n [formControl]=\"control()\"\n [placeholder]=\"field().placeholder || ''\"\n [readonly]=\"readonly() || field().readonly\">\n @if (field().hint) {\n <ngs-hint>{{ field().hint }}</ngs-hint>\n }\n </ngs-form-field>\n }\n }\n}\n", styles: [":host{display:block;min-width:0}:host.is-width-1{grid-column:span 1}:host.is-width-2{grid-column:span 2}:host.is-width-3{grid-column:span 3}:host.is-width-4{grid-column:span 4}:host.is-width-5{grid-column:span 5}:host.is-width-6{grid-column:span 6}:host.is-width-7{grid-column:span 7}:host.is-width-8{grid-column:span 8}:host.is-width-9{grid-column:span 9}:host.is-width-10{grid-column:span 10}:host.is-width-11{grid-column:span 11}:host.is-width-12{grid-column:span 12}:host .ngs-form-builder-check-field{display:flex;min-height:calc(var(--spacing, .25rem) * 11);flex-direction:column;justify-content:center;gap:calc(var(--spacing, .25rem) * 1)}:host .ngs-form-builder-check-field span{color:var(--ngs-color-on-surface-variant);font-size:var(--ngs-font-size-xs)}:host .ngs-form-builder-choice-field{display:flex;min-height:calc(var(--spacing, .25rem) * 11);flex-direction:column;gap:calc(var(--spacing, .25rem) * 2)}:host .ngs-form-builder-upload-field{display:flex;flex-direction:column;gap:calc(var(--spacing, .25rem) * 2)}:host .ngs-form-builder-upload-field.is-disabled{pointer-events:none;opacity:.7}:host .ngs-form-builder-upload-field ngs-upload-area{--ngs-upload-area-padding: calc(var(--spacing, .25rem) * 6);--ngs-upload-area-border-radius: var(--ngs-field-radius)}:host .ngs-form-builder-choice-label{color:var(--ngs-color-on-surface);font-size:var(--ngs-font-size-sm);font-weight:500}:host .ngs-form-builder-choice-hint{color:var(--ngs-color-on-surface-variant);font-size:var(--ngs-font-size-xs)}:host .ngs-form-builder-spacer{min-height:calc(var(--spacing, .25rem) * 6)}:host .ngs-form-builder-spacer.is-height-8{min-height:calc(var(--spacing, .25rem) * 2)}:host .ngs-form-builder-spacer.is-height-16{min-height:calc(var(--spacing, .25rem) * 4)}:host .ngs-form-builder-spacer.is-height-24{min-height:calc(var(--spacing, .25rem) * 6)}:host .ngs-form-builder-spacer.is-height-32{min-height:calc(var(--spacing, .25rem) * 8)}:host .ngs-form-builder-spacer.is-height-48{min-height:calc(var(--spacing, .25rem) * 12)}:host .ngs-form-builder-spacer.is-height-64{min-height:calc(var(--spacing, .25rem) * 16)}\n/*! tailwindcss v4.2.2 | MIT License | https://tailwindcss.com */\n"], dependencies: [{ kind: "ngmodule", type: ReactiveFormsModule }, { 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.NumberValueAccessor, selector: "input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]" }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.RequiredValidator, selector: ":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]", inputs: ["required"] }, { kind: "directive", type: i1.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }, { kind: "component", type: Datepicker, selector: "ngs-datepicker", inputs: ["startAt", "calendarHeaderComponent", "quickPresets", "showQuickPresets"], exportAs: ["ngsDatepicker"] }, { kind: "directive", type: DatepickerInput, selector: "input[ngsDatepicker]", inputs: ["ngsDatepicker"], exportAs: ["ngsDatepickerInput"] }, { kind: "component", type: DatepickerToggle, selector: "ngs-datepicker-toggle", inputs: ["for"], exportAs: ["ngsDatepickerToggle"] }, { kind: "component", type: DateRangeInput, selector: "ngs-date-range-input", inputs: ["rangePicker", "separator"], exportAs: ["ngsDateRangeInput"] }, { kind: "component", type: DateRangePicker, selector: "ngs-date-range-picker", inputs: ["startAt", "calendarHeaderComponent", "quickPresets", "showQuickPresets", "calendarCount", "extended"], exportAs: ["ngsDateRangePicker"] }, { kind: "directive", type: StartDate, selector: "input[ngsStartDate]" }, { kind: "directive", type: EndDate, selector: "input[ngsEndDate]" }, { 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: "directive", type: IconButtonSuffix, selector: "[ngsIconButtonSuffix]" }, { 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: Checkbox, selector: "ngs-checkbox", inputs: ["aria-label", "aria-labelledby", "aria-describedby", "aria-expanded", "aria-controls", "aria-owns", "id", "required", "labelPosition", "name", "value", "disableRipple", "tabIndex", "color", "disabledInteractive", "checked", "disabled", "indeterminate"], outputs: ["checkedChange", "disabledChange", "indeterminateChange", "change"], exportAs: ["ngsCheckbox"] }, { 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: RadioButton, selector: "ngs-radio-button", inputs: ["id", "value", "name", "checked", "disabled"], outputs: ["checkedChange", "change"] }, { kind: "component", type: RadioGroup, selector: "ngs-radio-group", inputs: ["disabled", "name", "orientation", "value"], outputs: ["disabledChange", "valueChange", "change"], exportAs: ["ngsRadioGroup"] }, { kind: "component", type: CountrySelect, selector: "ngs-country-select", inputs: ["searchTerm", "value", "id", "placeholder", "required", "disabled", "multiple", "hideCheckIcon", "clearable", "aria-label", "tabIndex", "aria-describedby", "showCountryCode"], outputs: ["searchTermChange", "valueChange", "requiredChange", "disabledChange", "opened", "closed", "selectionChange"], exportAs: ["ngsCountrySelect"] }, { kind: "component", type: CurrencySelect, selector: "ngs-currency-select", inputs: ["searchTerm", "placeholder", "required", "disabled", "showCountryName"], outputs: ["searchTermChange", "requiredChange", "disabledChange", "opened", "closed"], exportAs: ["ngsCurrencySelect"] }, { kind: "component", type: TimezoneSelect, selector: "ngs-timezone-select", inputs: ["aria-describedby", "locale", "placeholder", "required", "disabled", "value", "searchTerm"], outputs: ["valueChange", "searchTermChange", "opened", "closed"], exportAs: ["ngsTimezoneSelect"] }, { kind: "component", type: Timepicker, selector: "ngs-timepicker", inputs: ["disabled", "interval"], outputs: ["opened", "closed"], exportAs: ["ngsTimepicker"] }, { kind: "directive", type: TimepickerInput, selector: "input[ngsTimepicker]", inputs: ["disabled", "max", "min", "openOnClick", "ngsTimepicker"] }, { kind: "component", type: TimepickerToggle, selector: "ngs-timepicker-toggle", inputs: ["for", "disabled"], exportAs: ["ngsTimepickerToggle"] }, { kind: "component", type: Icon, selector: "ngs-icon", inputs: ["name"], exportAs: ["ngsIcon"] }, { kind: "component", type: UploadArea, selector: "ngs-upload-area", inputs: ["accept", "allowHover", "multiple"], outputs: ["fileSelected"], exportAs: ["ngsUploadArea"] }, { kind: "directive", type: UploadAreaIconDirective, selector: "[ngsUploadAreaIcon]" }, { kind: "directive", type: UploadAreaMainStateDirective, selector: "[ngsUploadAreaMainState]" }, { kind: "directive", type: UploadAreaDropStateDirective, selector: "[ngsUploadAreaDropState]" }, { kind: "directive", type: UploadAreaInvalidStateDirective, selector: "[ngsUploadAreaInvalidState]" }, { kind: "directive", type: UploadTriggerDirective, selector: "[ngsUploadTrigger]", inputs: ["accept", "multiple"], outputs: ["fileSelected"], exportAs: ["ngsUploadTrigger"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
789
+ ], viewQueries: [{ propertyName: "anchor", first: true, predicate: ["anchor"], descendants: true, read: ViewContainerRef, isSignal: true }], exportAs: ["ngsFormBuilderFieldHost"], ngImport: i0, template: "<ng-container #anchor/>\n\n@if (!customLoaded()) {\n @switch (field().type) {\n @case ('textarea') {\n <ngs-form-field>\n <ngs-label>{{ field().label }}</ngs-label>\n <textarea ngsInput\n rows=\"3\"\n [formControl]=\"control()\"\n [placeholder]=\"field().placeholder || ''\"\n [readonly]=\"readonly() || field().readonly\"></textarea>\n @if (field().hint) {\n <ngs-hint>{{ field().hint }}</ngs-hint>\n }\n </ngs-form-field>\n }\n\n @case ('hidden') {\n <input type=\"hidden\" [formControl]=\"control()\">\n }\n\n @case ('select') {\n <ngs-form-field>\n <ngs-label>{{ field().label }}</ngs-label>\n <ngs-select [formControl]=\"control()\"\n [placeholder]=\"field().placeholder || ''\"\n [multiple]=\"field().multiple ?? false\"\n [clearable]=\"field().clearable ?? false\">\n @for (option of field().options ?? []; track option.value) {\n <ngs-option [value]=\"option.value\">{{ option.label }}</ngs-option>\n }\n </ngs-select>\n @if (field().hint) {\n <ngs-hint>{{ field().hint }}</ngs-hint>\n }\n </ngs-form-field>\n }\n\n @case ('radio') {\n <div class=\"ngs-form-builder-choice-field\">\n <span class=\"ngs-form-builder-choice-label\">{{ field().label }}</span>\n <ngs-radio-group [formControl]=\"control()\"\n [name]=\"field().name\"\n [orientation]=\"radioOrientation()\">\n @for (option of field().options ?? []; track option.value) {\n <ngs-radio-button [value]=\"option.value\">{{ option.label }}</ngs-radio-button>\n }\n </ngs-radio-group>\n @if (field().hint) {\n <span class=\"ngs-form-builder-choice-hint\">{{ field().hint }}</span>\n }\n </div>\n }\n\n @case ('checkbox') {\n <div class=\"ngs-form-builder-check-field\">\n <ngs-checkbox [formControl]=\"control()\">\n {{ field().label }}\n </ngs-checkbox>\n @if (field().hint) {\n <span>{{ field().hint }}</span>\n }\n </div>\n }\n\n @case ('toggle') {\n <div class=\"ngs-form-builder-check-field\">\n <ngs-slide-toggle [formControl]=\"control()\">\n {{ field().label }}\n </ngs-slide-toggle>\n @if (field().hint) {\n <span>{{ field().hint }}</span>\n }\n </div>\n }\n\n @case ('date') {\n <ngs-form-field>\n <ngs-label>{{ field().label }}</ngs-label>\n <input ngsInput\n [ngsDatepicker]=\"picker\"\n [formControl]=\"control()\"\n [placeholder]=\"field().placeholder || ''\"\n [readonly]=\"readonly() || field().readonly\">\n <ngs-datepicker-toggle ngsIconButtonSuffix [for]=\"picker\"/>\n <ngs-datepicker #picker/>\n @if (field().hint) {\n <ngs-hint>{{ field().hint }}</ngs-hint>\n }\n </ngs-form-field>\n }\n\n @case ('time') {\n <ngs-form-field>\n <ngs-label>{{ field().label }}</ngs-label>\n <input ngsInput\n [ngsTimepicker]=\"timePicker\"\n [formControl]=\"control()\"\n [placeholder]=\"field().placeholder || ''\"\n [readonly]=\"readonly() || field().readonly\">\n <ngs-timepicker-toggle ngsIconButtonSuffix [for]=\"timePicker\"/>\n <ngs-timepicker #timePicker/>\n @if (field().hint) {\n <ngs-hint>{{ field().hint }}</ngs-hint>\n }\n </ngs-form-field>\n }\n\n @case ('date-range') {\n <ngs-form-field>\n <ngs-label>{{ field().label }}</ngs-label>\n <ngs-date-range-input #rangeInput [rangePicker]=\"rangePicker\">\n <input ngsStartDate\n [value]=\"dateRangeStartValue()\"\n [placeholder]=\"field().placeholder || 'Start date'\"\n [readonly]=\"readonly() || field().readonly\"\n [disabled]=\"dateRangeDisabled()\"\n (input)=\"onDateRangeChanged(rangeInput)\">\n <input ngsEndDate\n [value]=\"dateRangeEndValue()\"\n placeholder=\"End date\"\n [readonly]=\"readonly() || field().readonly\"\n [disabled]=\"dateRangeDisabled()\"\n (input)=\"onDateRangeChanged(rangeInput)\">\n </ngs-date-range-input>\n <ngs-datepicker-toggle ngsIconButtonSuffix [for]=\"rangePicker\"/>\n <ngs-date-range-picker #rangePicker/>\n @if (field().hint) {\n <ngs-hint>{{ field().hint }}</ngs-hint>\n }\n </ngs-form-field>\n }\n\n @case ('upload') {\n <div class=\"ngs-form-builder-upload-field\"\n [class.is-disabled]=\"uploadDisabled()\">\n <span class=\"ngs-form-builder-choice-label\">{{ field().label }}</span>\n <ngs-upload-area ngsUploadTrigger\n [accept]=\"uploadAccept()\"\n [multiple]=\"field().multiple ?? false\"\n (fileSelected)=\"onUploadFilesSelected($event)\">\n <ngs-icon name=\"fluent:arrow-upload-24-regular\" ngsUploadAreaIcon/>\n <ng-container ngsUploadAreaMainState>\n {{ uploadSelectedText() }}\n </ng-container>\n <ng-container ngsUploadAreaDropState>\n Drop files here\n </ng-container>\n <ng-container ngsUploadAreaInvalidState>\n Invalid file type\n </ng-container>\n </ngs-upload-area>\n @if (field().hint) {\n <span class=\"ngs-form-builder-choice-hint\">{{ field().hint }}</span>\n }\n </div>\n }\n\n @case ('spacer') {\n <div class=\"ngs-form-builder-spacer\"\n [class.is-height-8]=\"spacerHeight() === 8\"\n [class.is-height-16]=\"spacerHeight() === 16\"\n [class.is-height-24]=\"spacerHeight() === 24\"\n [class.is-height-32]=\"spacerHeight() === 32\"\n [class.is-height-48]=\"spacerHeight() === 48\"\n [class.is-height-64]=\"spacerHeight() === 64\"\n aria-hidden=\"true\"></div>\n }\n\n @case ('timezone-select') {\n <ngs-form-field>\n <ngs-label>{{ field().label }}</ngs-label>\n <ngs-timezone-select [formControl]=\"control()\"\n [placeholder]=\"field().placeholder || ''\"\n [required]=\"field().required ?? false\"/>\n @if (field().hint) {\n <ngs-hint>{{ field().hint }}</ngs-hint>\n }\n </ngs-form-field>\n }\n\n @case ('currency') {\n <ngs-form-field>\n <ngs-label>{{ field().label }}</ngs-label>\n <input ngsInput\n type=\"number\"\n inputmode=\"decimal\"\n [formControl]=\"control()\"\n [placeholder]=\"field().placeholder || ''\"\n [readonly]=\"readonly() || field().readonly\">\n @if (field().hint) {\n <ngs-hint>{{ field().hint }}</ngs-hint>\n }\n </ngs-form-field>\n }\n\n @case ('currency-select') {\n <ngs-form-field>\n <ngs-label>{{ field().label }}</ngs-label>\n <ngs-currency-select [formControl]=\"control()\"\n [placeholder]=\"field().placeholder || ''\"\n [required]=\"field().required ?? false\"/>\n @if (field().hint) {\n <ngs-hint>{{ field().hint }}</ngs-hint>\n }\n </ngs-form-field>\n }\n\n @case ('country-select') {\n <ngs-form-field>\n <ngs-label>{{ field().label }}</ngs-label>\n <ngs-country-select [formControl]=\"control()\"\n [placeholder]=\"field().placeholder || ''\"\n [clearable]=\"field().clearable ?? false\"\n [required]=\"field().required ?? false\"/>\n @if (field().hint) {\n <ngs-hint>{{ field().hint }}</ngs-hint>\n }\n </ngs-form-field>\n }\n\n @default {\n <ngs-form-field>\n <ngs-label>{{ field().label }}</ngs-label>\n <input ngsInput\n [type]=\"textInputType()\"\n [formControl]=\"control()\"\n [placeholder]=\"field().placeholder || ''\"\n [readonly]=\"readonly() || field().readonly\">\n @if (field().hint) {\n <ngs-hint>{{ field().hint }}</ngs-hint>\n }\n </ngs-form-field>\n }\n }\n}\n", styles: [":host{display:block;min-width:0}:host.is-hidden-field{display:none}:host.is-width-1{grid-column:span 1}:host.is-width-2{grid-column:span 2}:host.is-width-3{grid-column:span 3}:host.is-width-4{grid-column:span 4}:host.is-width-5{grid-column:span 5}:host.is-width-6{grid-column:span 6}:host.is-width-7{grid-column:span 7}:host.is-width-8{grid-column:span 8}:host.is-width-9{grid-column:span 9}:host.is-width-10{grid-column:span 10}:host.is-width-11{grid-column:span 11}:host.is-width-12{grid-column:span 12}:host .ngs-form-builder-check-field{display:flex;min-height:calc(var(--spacing, .25rem) * 11);flex-direction:column;justify-content:center;gap:calc(var(--spacing, .25rem) * 1)}:host .ngs-form-builder-check-field span{color:var(--ngs-color-on-surface-variant);font-size:var(--ngs-font-size-xs)}:host .ngs-form-builder-choice-field{display:flex;min-height:calc(var(--spacing, .25rem) * 11);flex-direction:column;gap:calc(var(--spacing, .25rem) * 2)}:host .ngs-form-builder-upload-field{display:flex;flex-direction:column;gap:calc(var(--spacing, .25rem) * 2)}:host .ngs-form-builder-upload-field.is-disabled{pointer-events:none;opacity:.7}:host .ngs-form-builder-upload-field ngs-upload-area{--ngs-upload-area-padding: calc(var(--spacing, .25rem) * 6);--ngs-upload-area-border-radius: var(--ngs-field-radius)}:host .ngs-form-builder-choice-label{color:var(--ngs-color-on-surface);font-size:var(--ngs-font-size-sm);font-weight:500}:host .ngs-form-builder-choice-hint{color:var(--ngs-color-on-surface-variant);font-size:var(--ngs-font-size-xs)}:host .ngs-form-builder-spacer{min-height:calc(var(--spacing, .25rem) * 6)}:host .ngs-form-builder-spacer.is-height-8{min-height:calc(var(--spacing, .25rem) * 2)}:host .ngs-form-builder-spacer.is-height-16{min-height:calc(var(--spacing, .25rem) * 4)}:host .ngs-form-builder-spacer.is-height-24{min-height:calc(var(--spacing, .25rem) * 6)}:host .ngs-form-builder-spacer.is-height-32{min-height:calc(var(--spacing, .25rem) * 8)}:host .ngs-form-builder-spacer.is-height-48{min-height:calc(var(--spacing, .25rem) * 12)}:host .ngs-form-builder-spacer.is-height-64{min-height:calc(var(--spacing, .25rem) * 16)}\n/*! tailwindcss v4.2.2 | MIT License | https://tailwindcss.com */\n"], dependencies: [{ kind: "ngmodule", type: ReactiveFormsModule }, { 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.NumberValueAccessor, selector: "input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]" }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.RequiredValidator, selector: ":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]", inputs: ["required"] }, { kind: "directive", type: i1.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }, { kind: "component", type: Datepicker, selector: "ngs-datepicker", inputs: ["startAt", "calendarHeaderComponent", "quickPresets", "showQuickPresets"], exportAs: ["ngsDatepicker"] }, { kind: "directive", type: DatepickerInput, selector: "input[ngsDatepicker]", inputs: ["ngsDatepicker"], exportAs: ["ngsDatepickerInput"] }, { kind: "component", type: DatepickerToggle, selector: "ngs-datepicker-toggle", inputs: ["for"], exportAs: ["ngsDatepickerToggle"] }, { kind: "component", type: DateRangeInput, selector: "ngs-date-range-input", inputs: ["rangePicker", "separator"], exportAs: ["ngsDateRangeInput"] }, { kind: "component", type: DateRangePicker, selector: "ngs-date-range-picker", inputs: ["startAt", "calendarHeaderComponent", "quickPresets", "showQuickPresets", "calendarCount", "extended"], exportAs: ["ngsDateRangePicker"] }, { kind: "directive", type: StartDate, selector: "input[ngsStartDate]" }, { kind: "directive", type: EndDate, selector: "input[ngsEndDate]" }, { 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: "directive", type: IconButtonSuffix, selector: "[ngsIconButtonSuffix]" }, { 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: Checkbox, selector: "ngs-checkbox", inputs: ["aria-label", "aria-labelledby", "aria-describedby", "aria-expanded", "aria-controls", "aria-owns", "id", "required", "labelPosition", "name", "value", "disableRipple", "tabIndex", "color", "disabledInteractive", "checked", "disabled", "indeterminate"], outputs: ["checkedChange", "disabledChange", "indeterminateChange", "change"], exportAs: ["ngsCheckbox"] }, { 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: RadioButton, selector: "ngs-radio-button", inputs: ["id", "value", "name", "checked", "disabled"], outputs: ["checkedChange", "change"] }, { kind: "component", type: RadioGroup, selector: "ngs-radio-group", inputs: ["disabled", "name", "orientation", "value"], outputs: ["disabledChange", "valueChange", "change"], exportAs: ["ngsRadioGroup"] }, { kind: "component", type: CountrySelect, selector: "ngs-country-select", inputs: ["searchTerm", "value", "id", "placeholder", "required", "disabled", "multiple", "hideCheckIcon", "clearable", "aria-label", "tabIndex", "aria-describedby", "showCountryCode"], outputs: ["searchTermChange", "valueChange", "requiredChange", "disabledChange", "opened", "closed", "selectionChange"], exportAs: ["ngsCountrySelect"] }, { kind: "component", type: CurrencySelect, selector: "ngs-currency-select", inputs: ["searchTerm", "placeholder", "required", "disabled", "showCountryName"], outputs: ["searchTermChange", "requiredChange", "disabledChange", "opened", "closed"], exportAs: ["ngsCurrencySelect"] }, { kind: "component", type: TimezoneSelect, selector: "ngs-timezone-select", inputs: ["aria-describedby", "locale", "placeholder", "required", "disabled", "value", "searchTerm"], outputs: ["valueChange", "searchTermChange", "opened", "closed"], exportAs: ["ngsTimezoneSelect"] }, { kind: "component", type: Timepicker, selector: "ngs-timepicker", inputs: ["disabled", "interval"], outputs: ["opened", "closed"], exportAs: ["ngsTimepicker"] }, { kind: "directive", type: TimepickerInput, selector: "input[ngsTimepicker]", inputs: ["disabled", "max", "min", "openOnClick", "ngsTimepicker"] }, { kind: "component", type: TimepickerToggle, selector: "ngs-timepicker-toggle", inputs: ["for", "disabled"], exportAs: ["ngsTimepickerToggle"] }, { kind: "component", type: Icon, selector: "ngs-icon", inputs: ["name"], exportAs: ["ngsIcon"] }, { kind: "component", type: UploadArea, selector: "ngs-upload-area", inputs: ["accept", "allowHover", "multiple"], outputs: ["fileSelected"], exportAs: ["ngsUploadArea"] }, { kind: "directive", type: UploadAreaIconDirective, selector: "[ngsUploadAreaIcon]" }, { kind: "directive", type: UploadAreaMainStateDirective, selector: "[ngsUploadAreaMainState]" }, { kind: "directive", type: UploadAreaDropStateDirective, selector: "[ngsUploadAreaDropState]" }, { kind: "directive", type: UploadAreaInvalidStateDirective, selector: "[ngsUploadAreaInvalidState]" }, { kind: "directive", type: UploadTriggerDirective, selector: "[ngsUploadTrigger]", inputs: ["accept", "multiple"], outputs: ["fileSelected"], exportAs: ["ngsUploadTrigger"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
436
790
  }
437
791
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: FormBuilderFieldHost, decorators: [{
438
792
  type: Component,
@@ -485,8 +839,9 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.4", ngImpor
485
839
  '[class.is-width-9]': '!editableCanvas() && field().width === 9',
486
840
  '[class.is-width-10]': '!editableCanvas() && field().width === 10',
487
841
  '[class.is-width-11]': '!editableCanvas() && field().width === 11',
488
- '[class.is-width-12]': '!editableCanvas() && (field().width ?? 12) === 12'
489
- }, template: "<ng-container #anchor/>\n\n@if (!customLoaded()) {\n @switch (field().type) {\n @case ('textarea') {\n <ngs-form-field>\n <ngs-label>{{ field().label }}</ngs-label>\n <textarea ngsInput\n rows=\"3\"\n [formControl]=\"control()\"\n [placeholder]=\"field().placeholder || ''\"\n [readonly]=\"readonly() || field().readonly\"></textarea>\n @if (field().hint) {\n <ngs-hint>{{ field().hint }}</ngs-hint>\n }\n </ngs-form-field>\n }\n\n @case ('select') {\n <ngs-form-field>\n <ngs-label>{{ field().label }}</ngs-label>\n <ngs-select [formControl]=\"control()\"\n [placeholder]=\"field().placeholder || ''\"\n [multiple]=\"field().multiple ?? false\"\n [clearable]=\"field().clearable ?? false\">\n @for (option of field().options ?? []; track option.value) {\n <ngs-option [value]=\"option.value\">{{ option.label }}</ngs-option>\n }\n </ngs-select>\n @if (field().hint) {\n <ngs-hint>{{ field().hint }}</ngs-hint>\n }\n </ngs-form-field>\n }\n\n @case ('radio') {\n <div class=\"ngs-form-builder-choice-field\">\n <span class=\"ngs-form-builder-choice-label\">{{ field().label }}</span>\n <ngs-radio-group [formControl]=\"control()\"\n [name]=\"field().name\"\n [orientation]=\"radioOrientation()\">\n @for (option of field().options ?? []; track option.value) {\n <ngs-radio-button [value]=\"option.value\">{{ option.label }}</ngs-radio-button>\n }\n </ngs-radio-group>\n @if (field().hint) {\n <span class=\"ngs-form-builder-choice-hint\">{{ field().hint }}</span>\n }\n </div>\n }\n\n @case ('checkbox') {\n <div class=\"ngs-form-builder-check-field\">\n <ngs-checkbox [formControl]=\"control()\">\n {{ field().label }}\n </ngs-checkbox>\n @if (field().hint) {\n <span>{{ field().hint }}</span>\n }\n </div>\n }\n\n @case ('toggle') {\n <div class=\"ngs-form-builder-check-field\">\n <ngs-slide-toggle [formControl]=\"control()\">\n {{ field().label }}\n </ngs-slide-toggle>\n @if (field().hint) {\n <span>{{ field().hint }}</span>\n }\n </div>\n }\n\n @case ('date') {\n <ngs-form-field>\n <ngs-label>{{ field().label }}</ngs-label>\n <input ngsInput\n [ngsDatepicker]=\"picker\"\n [formControl]=\"control()\"\n [placeholder]=\"field().placeholder || ''\"\n [readonly]=\"readonly() || field().readonly\">\n <ngs-datepicker-toggle ngsIconButtonSuffix [for]=\"picker\"/>\n <ngs-datepicker #picker/>\n @if (field().hint) {\n <ngs-hint>{{ field().hint }}</ngs-hint>\n }\n </ngs-form-field>\n }\n\n @case ('time') {\n <ngs-form-field>\n <ngs-label>{{ field().label }}</ngs-label>\n <input ngsInput\n [ngsTimepicker]=\"timePicker\"\n [formControl]=\"control()\"\n [placeholder]=\"field().placeholder || ''\"\n [readonly]=\"readonly() || field().readonly\">\n <ngs-timepicker-toggle ngsIconButtonSuffix [for]=\"timePicker\"/>\n <ngs-timepicker #timePicker/>\n @if (field().hint) {\n <ngs-hint>{{ field().hint }}</ngs-hint>\n }\n </ngs-form-field>\n }\n\n @case ('date-range') {\n <ngs-form-field>\n <ngs-label>{{ field().label }}</ngs-label>\n <ngs-date-range-input #rangeInput [rangePicker]=\"rangePicker\">\n <input ngsStartDate\n [value]=\"dateRangeStartValue()\"\n [placeholder]=\"field().placeholder || 'Start date'\"\n [readonly]=\"readonly() || field().readonly\"\n [disabled]=\"dateRangeDisabled()\"\n (input)=\"onDateRangeChanged(rangeInput)\">\n <input ngsEndDate\n [value]=\"dateRangeEndValue()\"\n placeholder=\"End date\"\n [readonly]=\"readonly() || field().readonly\"\n [disabled]=\"dateRangeDisabled()\"\n (input)=\"onDateRangeChanged(rangeInput)\">\n </ngs-date-range-input>\n <ngs-datepicker-toggle ngsIconButtonSuffix [for]=\"rangePicker\"/>\n <ngs-date-range-picker #rangePicker/>\n @if (field().hint) {\n <ngs-hint>{{ field().hint }}</ngs-hint>\n }\n </ngs-form-field>\n }\n\n @case ('upload') {\n <div class=\"ngs-form-builder-upload-field\"\n [class.is-disabled]=\"uploadDisabled()\">\n <span class=\"ngs-form-builder-choice-label\">{{ field().label }}</span>\n <ngs-upload-area ngsUploadTrigger\n [accept]=\"uploadAccept()\"\n [multiple]=\"field().multiple ?? false\"\n (fileSelected)=\"onUploadFilesSelected($event)\">\n <ngs-icon name=\"fluent:arrow-upload-24-regular\" ngsUploadAreaIcon/>\n <ng-container ngsUploadAreaMainState>\n {{ uploadSelectedText() }}\n </ng-container>\n <ng-container ngsUploadAreaDropState>\n Drop files here\n </ng-container>\n <ng-container ngsUploadAreaInvalidState>\n Invalid file type\n </ng-container>\n </ngs-upload-area>\n @if (field().hint) {\n <span class=\"ngs-form-builder-choice-hint\">{{ field().hint }}</span>\n }\n </div>\n }\n\n @case ('spacer') {\n <div class=\"ngs-form-builder-spacer\"\n [class.is-height-8]=\"spacerHeight() === 8\"\n [class.is-height-16]=\"spacerHeight() === 16\"\n [class.is-height-24]=\"spacerHeight() === 24\"\n [class.is-height-32]=\"spacerHeight() === 32\"\n [class.is-height-48]=\"spacerHeight() === 48\"\n [class.is-height-64]=\"spacerHeight() === 64\"\n aria-hidden=\"true\"></div>\n }\n\n @case ('timezone-select') {\n <ngs-form-field>\n <ngs-label>{{ field().label }}</ngs-label>\n <ngs-timezone-select [formControl]=\"control()\"\n [placeholder]=\"field().placeholder || ''\"\n [required]=\"field().required ?? false\"/>\n @if (field().hint) {\n <ngs-hint>{{ field().hint }}</ngs-hint>\n }\n </ngs-form-field>\n }\n\n @case ('currency') {\n <ngs-form-field>\n <ngs-label>{{ field().label }}</ngs-label>\n <input ngsInput\n type=\"number\"\n inputmode=\"decimal\"\n [formControl]=\"control()\"\n [placeholder]=\"field().placeholder || ''\"\n [readonly]=\"readonly() || field().readonly\">\n @if (field().hint) {\n <ngs-hint>{{ field().hint }}</ngs-hint>\n }\n </ngs-form-field>\n }\n\n @case ('currency-select') {\n <ngs-form-field>\n <ngs-label>{{ field().label }}</ngs-label>\n <ngs-currency-select [formControl]=\"control()\"\n [placeholder]=\"field().placeholder || ''\"\n [required]=\"field().required ?? false\"/>\n @if (field().hint) {\n <ngs-hint>{{ field().hint }}</ngs-hint>\n }\n </ngs-form-field>\n }\n\n @case ('country-select') {\n <ngs-form-field>\n <ngs-label>{{ field().label }}</ngs-label>\n <ngs-country-select [formControl]=\"control()\"\n [placeholder]=\"field().placeholder || ''\"\n [clearable]=\"field().clearable ?? false\"\n [required]=\"field().required ?? false\"/>\n @if (field().hint) {\n <ngs-hint>{{ field().hint }}</ngs-hint>\n }\n </ngs-form-field>\n }\n\n @default {\n <ngs-form-field>\n <ngs-label>{{ field().label }}</ngs-label>\n <input ngsInput\n [type]=\"textInputType()\"\n [formControl]=\"control()\"\n [placeholder]=\"field().placeholder || ''\"\n [readonly]=\"readonly() || field().readonly\">\n @if (field().hint) {\n <ngs-hint>{{ field().hint }}</ngs-hint>\n }\n </ngs-form-field>\n }\n }\n}\n", styles: [":host{display:block;min-width:0}:host.is-width-1{grid-column:span 1}:host.is-width-2{grid-column:span 2}:host.is-width-3{grid-column:span 3}:host.is-width-4{grid-column:span 4}:host.is-width-5{grid-column:span 5}:host.is-width-6{grid-column:span 6}:host.is-width-7{grid-column:span 7}:host.is-width-8{grid-column:span 8}:host.is-width-9{grid-column:span 9}:host.is-width-10{grid-column:span 10}:host.is-width-11{grid-column:span 11}:host.is-width-12{grid-column:span 12}:host .ngs-form-builder-check-field{display:flex;min-height:calc(var(--spacing, .25rem) * 11);flex-direction:column;justify-content:center;gap:calc(var(--spacing, .25rem) * 1)}:host .ngs-form-builder-check-field span{color:var(--ngs-color-on-surface-variant);font-size:var(--ngs-font-size-xs)}:host .ngs-form-builder-choice-field{display:flex;min-height:calc(var(--spacing, .25rem) * 11);flex-direction:column;gap:calc(var(--spacing, .25rem) * 2)}:host .ngs-form-builder-upload-field{display:flex;flex-direction:column;gap:calc(var(--spacing, .25rem) * 2)}:host .ngs-form-builder-upload-field.is-disabled{pointer-events:none;opacity:.7}:host .ngs-form-builder-upload-field ngs-upload-area{--ngs-upload-area-padding: calc(var(--spacing, .25rem) * 6);--ngs-upload-area-border-radius: var(--ngs-field-radius)}:host .ngs-form-builder-choice-label{color:var(--ngs-color-on-surface);font-size:var(--ngs-font-size-sm);font-weight:500}:host .ngs-form-builder-choice-hint{color:var(--ngs-color-on-surface-variant);font-size:var(--ngs-font-size-xs)}:host .ngs-form-builder-spacer{min-height:calc(var(--spacing, .25rem) * 6)}:host .ngs-form-builder-spacer.is-height-8{min-height:calc(var(--spacing, .25rem) * 2)}:host .ngs-form-builder-spacer.is-height-16{min-height:calc(var(--spacing, .25rem) * 4)}:host .ngs-form-builder-spacer.is-height-24{min-height:calc(var(--spacing, .25rem) * 6)}:host .ngs-form-builder-spacer.is-height-32{min-height:calc(var(--spacing, .25rem) * 8)}:host .ngs-form-builder-spacer.is-height-48{min-height:calc(var(--spacing, .25rem) * 12)}:host .ngs-form-builder-spacer.is-height-64{min-height:calc(var(--spacing, .25rem) * 16)}\n/*! tailwindcss v4.2.2 | MIT License | https://tailwindcss.com */\n"] }]
842
+ '[class.is-width-12]': '!editableCanvas() && (field().width ?? 12) === 12',
843
+ '[class.is-hidden-field]': '!editableCanvas() && field().type === "hidden"'
844
+ }, template: "<ng-container #anchor/>\n\n@if (!customLoaded()) {\n @switch (field().type) {\n @case ('textarea') {\n <ngs-form-field>\n <ngs-label>{{ field().label }}</ngs-label>\n <textarea ngsInput\n rows=\"3\"\n [formControl]=\"control()\"\n [placeholder]=\"field().placeholder || ''\"\n [readonly]=\"readonly() || field().readonly\"></textarea>\n @if (field().hint) {\n <ngs-hint>{{ field().hint }}</ngs-hint>\n }\n </ngs-form-field>\n }\n\n @case ('hidden') {\n <input type=\"hidden\" [formControl]=\"control()\">\n }\n\n @case ('select') {\n <ngs-form-field>\n <ngs-label>{{ field().label }}</ngs-label>\n <ngs-select [formControl]=\"control()\"\n [placeholder]=\"field().placeholder || ''\"\n [multiple]=\"field().multiple ?? false\"\n [clearable]=\"field().clearable ?? false\">\n @for (option of field().options ?? []; track option.value) {\n <ngs-option [value]=\"option.value\">{{ option.label }}</ngs-option>\n }\n </ngs-select>\n @if (field().hint) {\n <ngs-hint>{{ field().hint }}</ngs-hint>\n }\n </ngs-form-field>\n }\n\n @case ('radio') {\n <div class=\"ngs-form-builder-choice-field\">\n <span class=\"ngs-form-builder-choice-label\">{{ field().label }}</span>\n <ngs-radio-group [formControl]=\"control()\"\n [name]=\"field().name\"\n [orientation]=\"radioOrientation()\">\n @for (option of field().options ?? []; track option.value) {\n <ngs-radio-button [value]=\"option.value\">{{ option.label }}</ngs-radio-button>\n }\n </ngs-radio-group>\n @if (field().hint) {\n <span class=\"ngs-form-builder-choice-hint\">{{ field().hint }}</span>\n }\n </div>\n }\n\n @case ('checkbox') {\n <div class=\"ngs-form-builder-check-field\">\n <ngs-checkbox [formControl]=\"control()\">\n {{ field().label }}\n </ngs-checkbox>\n @if (field().hint) {\n <span>{{ field().hint }}</span>\n }\n </div>\n }\n\n @case ('toggle') {\n <div class=\"ngs-form-builder-check-field\">\n <ngs-slide-toggle [formControl]=\"control()\">\n {{ field().label }}\n </ngs-slide-toggle>\n @if (field().hint) {\n <span>{{ field().hint }}</span>\n }\n </div>\n }\n\n @case ('date') {\n <ngs-form-field>\n <ngs-label>{{ field().label }}</ngs-label>\n <input ngsInput\n [ngsDatepicker]=\"picker\"\n [formControl]=\"control()\"\n [placeholder]=\"field().placeholder || ''\"\n [readonly]=\"readonly() || field().readonly\">\n <ngs-datepicker-toggle ngsIconButtonSuffix [for]=\"picker\"/>\n <ngs-datepicker #picker/>\n @if (field().hint) {\n <ngs-hint>{{ field().hint }}</ngs-hint>\n }\n </ngs-form-field>\n }\n\n @case ('time') {\n <ngs-form-field>\n <ngs-label>{{ field().label }}</ngs-label>\n <input ngsInput\n [ngsTimepicker]=\"timePicker\"\n [formControl]=\"control()\"\n [placeholder]=\"field().placeholder || ''\"\n [readonly]=\"readonly() || field().readonly\">\n <ngs-timepicker-toggle ngsIconButtonSuffix [for]=\"timePicker\"/>\n <ngs-timepicker #timePicker/>\n @if (field().hint) {\n <ngs-hint>{{ field().hint }}</ngs-hint>\n }\n </ngs-form-field>\n }\n\n @case ('date-range') {\n <ngs-form-field>\n <ngs-label>{{ field().label }}</ngs-label>\n <ngs-date-range-input #rangeInput [rangePicker]=\"rangePicker\">\n <input ngsStartDate\n [value]=\"dateRangeStartValue()\"\n [placeholder]=\"field().placeholder || 'Start date'\"\n [readonly]=\"readonly() || field().readonly\"\n [disabled]=\"dateRangeDisabled()\"\n (input)=\"onDateRangeChanged(rangeInput)\">\n <input ngsEndDate\n [value]=\"dateRangeEndValue()\"\n placeholder=\"End date\"\n [readonly]=\"readonly() || field().readonly\"\n [disabled]=\"dateRangeDisabled()\"\n (input)=\"onDateRangeChanged(rangeInput)\">\n </ngs-date-range-input>\n <ngs-datepicker-toggle ngsIconButtonSuffix [for]=\"rangePicker\"/>\n <ngs-date-range-picker #rangePicker/>\n @if (field().hint) {\n <ngs-hint>{{ field().hint }}</ngs-hint>\n }\n </ngs-form-field>\n }\n\n @case ('upload') {\n <div class=\"ngs-form-builder-upload-field\"\n [class.is-disabled]=\"uploadDisabled()\">\n <span class=\"ngs-form-builder-choice-label\">{{ field().label }}</span>\n <ngs-upload-area ngsUploadTrigger\n [accept]=\"uploadAccept()\"\n [multiple]=\"field().multiple ?? false\"\n (fileSelected)=\"onUploadFilesSelected($event)\">\n <ngs-icon name=\"fluent:arrow-upload-24-regular\" ngsUploadAreaIcon/>\n <ng-container ngsUploadAreaMainState>\n {{ uploadSelectedText() }}\n </ng-container>\n <ng-container ngsUploadAreaDropState>\n Drop files here\n </ng-container>\n <ng-container ngsUploadAreaInvalidState>\n Invalid file type\n </ng-container>\n </ngs-upload-area>\n @if (field().hint) {\n <span class=\"ngs-form-builder-choice-hint\">{{ field().hint }}</span>\n }\n </div>\n }\n\n @case ('spacer') {\n <div class=\"ngs-form-builder-spacer\"\n [class.is-height-8]=\"spacerHeight() === 8\"\n [class.is-height-16]=\"spacerHeight() === 16\"\n [class.is-height-24]=\"spacerHeight() === 24\"\n [class.is-height-32]=\"spacerHeight() === 32\"\n [class.is-height-48]=\"spacerHeight() === 48\"\n [class.is-height-64]=\"spacerHeight() === 64\"\n aria-hidden=\"true\"></div>\n }\n\n @case ('timezone-select') {\n <ngs-form-field>\n <ngs-label>{{ field().label }}</ngs-label>\n <ngs-timezone-select [formControl]=\"control()\"\n [placeholder]=\"field().placeholder || ''\"\n [required]=\"field().required ?? false\"/>\n @if (field().hint) {\n <ngs-hint>{{ field().hint }}</ngs-hint>\n }\n </ngs-form-field>\n }\n\n @case ('currency') {\n <ngs-form-field>\n <ngs-label>{{ field().label }}</ngs-label>\n <input ngsInput\n type=\"number\"\n inputmode=\"decimal\"\n [formControl]=\"control()\"\n [placeholder]=\"field().placeholder || ''\"\n [readonly]=\"readonly() || field().readonly\">\n @if (field().hint) {\n <ngs-hint>{{ field().hint }}</ngs-hint>\n }\n </ngs-form-field>\n }\n\n @case ('currency-select') {\n <ngs-form-field>\n <ngs-label>{{ field().label }}</ngs-label>\n <ngs-currency-select [formControl]=\"control()\"\n [placeholder]=\"field().placeholder || ''\"\n [required]=\"field().required ?? false\"/>\n @if (field().hint) {\n <ngs-hint>{{ field().hint }}</ngs-hint>\n }\n </ngs-form-field>\n }\n\n @case ('country-select') {\n <ngs-form-field>\n <ngs-label>{{ field().label }}</ngs-label>\n <ngs-country-select [formControl]=\"control()\"\n [placeholder]=\"field().placeholder || ''\"\n [clearable]=\"field().clearable ?? false\"\n [required]=\"field().required ?? false\"/>\n @if (field().hint) {\n <ngs-hint>{{ field().hint }}</ngs-hint>\n }\n </ngs-form-field>\n }\n\n @default {\n <ngs-form-field>\n <ngs-label>{{ field().label }}</ngs-label>\n <input ngsInput\n [type]=\"textInputType()\"\n [formControl]=\"control()\"\n [placeholder]=\"field().placeholder || ''\"\n [readonly]=\"readonly() || field().readonly\">\n @if (field().hint) {\n <ngs-hint>{{ field().hint }}</ngs-hint>\n }\n </ngs-form-field>\n }\n }\n}\n", styles: [":host{display:block;min-width:0}:host.is-hidden-field{display:none}:host.is-width-1{grid-column:span 1}:host.is-width-2{grid-column:span 2}:host.is-width-3{grid-column:span 3}:host.is-width-4{grid-column:span 4}:host.is-width-5{grid-column:span 5}:host.is-width-6{grid-column:span 6}:host.is-width-7{grid-column:span 7}:host.is-width-8{grid-column:span 8}:host.is-width-9{grid-column:span 9}:host.is-width-10{grid-column:span 10}:host.is-width-11{grid-column:span 11}:host.is-width-12{grid-column:span 12}:host .ngs-form-builder-check-field{display:flex;min-height:calc(var(--spacing, .25rem) * 11);flex-direction:column;justify-content:center;gap:calc(var(--spacing, .25rem) * 1)}:host .ngs-form-builder-check-field span{color:var(--ngs-color-on-surface-variant);font-size:var(--ngs-font-size-xs)}:host .ngs-form-builder-choice-field{display:flex;min-height:calc(var(--spacing, .25rem) * 11);flex-direction:column;gap:calc(var(--spacing, .25rem) * 2)}:host .ngs-form-builder-upload-field{display:flex;flex-direction:column;gap:calc(var(--spacing, .25rem) * 2)}:host .ngs-form-builder-upload-field.is-disabled{pointer-events:none;opacity:.7}:host .ngs-form-builder-upload-field ngs-upload-area{--ngs-upload-area-padding: calc(var(--spacing, .25rem) * 6);--ngs-upload-area-border-radius: var(--ngs-field-radius)}:host .ngs-form-builder-choice-label{color:var(--ngs-color-on-surface);font-size:var(--ngs-font-size-sm);font-weight:500}:host .ngs-form-builder-choice-hint{color:var(--ngs-color-on-surface-variant);font-size:var(--ngs-font-size-xs)}:host .ngs-form-builder-spacer{min-height:calc(var(--spacing, .25rem) * 6)}:host .ngs-form-builder-spacer.is-height-8{min-height:calc(var(--spacing, .25rem) * 2)}:host .ngs-form-builder-spacer.is-height-16{min-height:calc(var(--spacing, .25rem) * 4)}:host .ngs-form-builder-spacer.is-height-24{min-height:calc(var(--spacing, .25rem) * 6)}:host .ngs-form-builder-spacer.is-height-32{min-height:calc(var(--spacing, .25rem) * 8)}:host .ngs-form-builder-spacer.is-height-48{min-height:calc(var(--spacing, .25rem) * 12)}:host .ngs-form-builder-spacer.is-height-64{min-height:calc(var(--spacing, .25rem) * 16)}\n/*! tailwindcss v4.2.2 | MIT License | https://tailwindcss.com */\n"] }]
490
845
  }], ctorParameters: () => [], propDecorators: { field: [{ type: i0.Input, args: [{ isSignal: true, alias: "field", required: true }] }], control: [{ type: i0.Input, args: [{ isSignal: true, alias: "control", required: true }] }], definitions: [{ type: i0.Input, args: [{ isSignal: true, alias: "definitions", required: false }] }], readonly: [{ type: i0.Input, args: [{ isSignal: true, alias: "readonly", required: false }] }], editableCanvas: [{ type: i0.Input, args: [{ isSignal: true, alias: "editableCanvas", required: false }] }], uploadCallback: [{ type: i0.Input, args: [{ isSignal: true, alias: "uploadCallback", required: false }] }], anchor: [{ type: i0.ViewChild, args: ['anchor', { ...{ read: ViewContainerRef }, isSignal: true }] }] } });
491
846
 
492
847
  class FormBuilderRenderer {
@@ -523,23 +878,7 @@ class FormBuilderRenderer {
523
878
  }
524
879
  return definitions;
525
880
  }, []), ...(ngDevMode ? [{ debugName: "definitions" }] : /* istanbul ignore next */ []));
526
- visibleCanvasItems = computed(() => this.resolveCanvasItems(this.schema())
527
- .map(item => {
528
- if (item.field) {
529
- return {
530
- ...item,
531
- field: this.visibleFields([item.field])[0]
532
- };
533
- }
534
- return {
535
- ...item,
536
- section: {
537
- ...item.section,
538
- fields: this.visibleFields(item.section.fields)
539
- }
540
- };
541
- })
542
- .filter(item => !!item.field || !!item.section?.fields.length), ...(ngDevMode ? [{ debugName: "visibleCanvasItems" }] : /* istanbul ignore next */ []));
881
+ visibleCanvasItems = computed(() => this.resolveCanvasItems(this.schema()).filter(item => !!item.field || !!item.section?.fields.length), ...(ngDevMode ? [{ debugName: "visibleCanvasItems" }] : /* istanbul ignore next */ []));
543
882
  formGroup = computed(() => this.createFormGroup(), ...(ngDevMode ? [{ debugName: "formGroup" }] : /* istanbul ignore next */ []));
544
883
  constructor() {
545
884
  effect(onCleanup => {
@@ -551,8 +890,8 @@ class FormBuilderRenderer {
551
890
  onCleanup(() => subscription.unsubscribe());
552
891
  });
553
892
  }
554
- getControl(field) {
555
- const control = this.formGroup().get(field.name);
893
+ getControl(field, formGroup = this.formGroup()) {
894
+ const control = formGroup.controls[field.name];
556
895
  if (control instanceof FormControl) {
557
896
  return control;
558
897
  }
@@ -576,7 +915,45 @@ class FormBuilderRenderer {
576
915
  field.type === 'grid';
577
916
  }
578
917
  visibleChildren(field) {
579
- return this.visibleFields(field.children ?? []);
918
+ return field.children ?? [];
919
+ }
920
+ isRepeaterField(field) {
921
+ return field.type === 'repeater';
922
+ }
923
+ repeaterArray(field, formGroup = this.formGroup()) {
924
+ const control = formGroup.controls[field.name];
925
+ return control instanceof FormArray
926
+ ? control
927
+ : new FormArray([]);
928
+ }
929
+ repeaterGroups(field, formGroup = this.formGroup()) {
930
+ return this.repeaterArray(field, formGroup).controls;
931
+ }
932
+ repeaterEmptyText(field) {
933
+ const emptyText = field.settings?.['emptyText'];
934
+ return typeof emptyText === 'string' ? emptyText.trim() : '';
935
+ }
936
+ addRepeaterItem(field, formGroup = this.formGroup()) {
937
+ if (this.readonly()) {
938
+ return;
939
+ }
940
+ const array = this.repeaterArray(field, formGroup);
941
+ array.push(this.createRepeaterGroup(field));
942
+ array.updateValueAndValidity();
943
+ }
944
+ removeRepeaterItem(field, index, formGroup = this.formGroup()) {
945
+ if (this.readonly()) {
946
+ return;
947
+ }
948
+ if (!this.canRemoveRepeaterItem(field, formGroup)) {
949
+ return;
950
+ }
951
+ const array = this.repeaterArray(field, formGroup);
952
+ array.removeAt(index);
953
+ array.updateValueAndValidity();
954
+ }
955
+ canRemoveRepeaterItem(field, formGroup = this.formGroup()) {
956
+ return this.allowsNullValue(field) || this.repeaterArray(field, formGroup).length > 1;
580
957
  }
581
958
  submit() {
582
959
  const form = this.formGroup();
@@ -590,12 +967,24 @@ class FormBuilderRenderer {
590
967
  const controls = {};
591
968
  const value = this.value();
592
969
  const fields = [
593
- ...flattenFields(this.schema().fields ?? []),
594
- ...this.schema().sections.flatMap(section => flattenFields(section.fields))
970
+ ...(this.schema().fields ?? []),
971
+ ...this.schema().sections.flatMap(section => section.fields)
595
972
  ];
973
+ this.addFieldsToControls(controls, fields, value);
974
+ return new FormGroup(controls);
975
+ }
976
+ addFieldsToControls(controls, fields, value) {
596
977
  for (const field of fields) {
597
978
  const definition = this.definitions().find(item => item.type === field.type);
598
- if (this.isContainerField(field) || definition?.kind === 'static' || field.kind === 'static') {
979
+ if (this.isRepeaterField(field)) {
980
+ controls[field.name] = this.createRepeaterArray(field, value[field.name]);
981
+ continue;
982
+ }
983
+ if (this.isContainerField(field)) {
984
+ this.addFieldsToControls(controls, field.children ?? [], value);
985
+ continue;
986
+ }
987
+ if (definition?.kind === 'static' || field.kind === 'static') {
599
988
  continue;
600
989
  }
601
990
  const validators = definition?.validators?.(field) ?? validatorsFromRules(field.validation, field);
@@ -605,15 +994,32 @@ class FormBuilderRenderer {
605
994
  }, validators);
606
995
  controls[field.name] = control;
607
996
  }
608
- return new FormGroup(controls);
609
997
  }
610
- visibleFields(fields) {
611
- return fields
612
- .filter(field => field.visibility?.form !== false)
613
- .map(field => ({
614
- ...field,
615
- children: field.children ? this.visibleFields(field.children) : undefined
616
- }));
998
+ createRepeaterArray(field, value) {
999
+ const allowNullValue = this.allowsNullValue(field);
1000
+ const rows = Array.isArray(value) && (allowNullValue || value.length > 0)
1001
+ ? value
1002
+ : allowNullValue
1003
+ ? []
1004
+ : [{}];
1005
+ const validators = allowNullValue ? [] : [repeaterRequiredValidator];
1006
+ return new FormArray(rows.map(row => this.createRepeaterGroup(field, row)), validators);
1007
+ }
1008
+ createRepeaterGroup(field, value = {}) {
1009
+ const controls = {};
1010
+ const rowValue = isRecord(value) ? value : {};
1011
+ for (const child of flattenFields(field.children ?? [])) {
1012
+ const definition = this.definitions().find(item => item.type === child.type);
1013
+ if (this.isContainerField(child) || definition?.kind === 'static' || child.kind === 'static') {
1014
+ continue;
1015
+ }
1016
+ const validators = definition?.validators?.(child) ?? validatorsFromRules(child.validation, child);
1017
+ controls[child.name] = new FormControl({
1018
+ value: rowValue[child.name] ?? this.fieldInitialValue(child),
1019
+ disabled: child.disabled || this.readonly()
1020
+ }, validators);
1021
+ }
1022
+ return new FormGroup(controls);
617
1023
  }
618
1024
  fieldInitialValue(field) {
619
1025
  if (field.defaultValue !== undefined) {
@@ -630,6 +1036,9 @@ class FormBuilderRenderer {
630
1036
  }
631
1037
  return selectedValues[0] ?? null;
632
1038
  }
1039
+ allowsNullValue(field) {
1040
+ return field.settings?.['allowNullValue'] === true;
1041
+ }
633
1042
  resolveCanvasItems(schema) {
634
1043
  const fieldsById = new Map((schema.fields ?? []).map(field => [field.id, field]));
635
1044
  const sectionsById = new Map(schema.sections.map(section => [section.id, section]));
@@ -650,7 +1059,7 @@ class FormBuilderRenderer {
650
1059
  return items;
651
1060
  }
652
1061
  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 });
1062
+ 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
1063
  }
655
1064
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: FormBuilderRenderer, decorators: [{
656
1065
  type: Component,
@@ -661,7 +1070,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.4", ngImpor
661
1070
  FormBuilderFieldHost
662
1071
  ], changeDetection: ChangeDetectionStrategy.OnPush, host: {
663
1072
  '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"] }]
1073
+ }, 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
1074
  }], 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
1075
  function normalizeFieldDefinition$1(definition) {
667
1076
  return {
@@ -672,6 +1081,14 @@ function normalizeFieldDefinition$1(definition) {
672
1081
  function flattenFields(fields) {
673
1082
  return fields.flatMap(field => [field, ...flattenFields(field.children ?? [])]);
674
1083
  }
1084
+ function isRecord(value) {
1085
+ return typeof value === 'object' && value !== null && !Array.isArray(value);
1086
+ }
1087
+ function repeaterRequiredValidator(control) {
1088
+ return control instanceof FormArray && control.length === 0
1089
+ ? { repeaterRequired: true }
1090
+ : null;
1091
+ }
675
1092
  function normalizedLayout(schema) {
676
1093
  const used = new Set();
677
1094
  const layout = [];
@@ -704,250 +1121,6 @@ function normalizedLayout(schema) {
704
1121
  return layout;
705
1122
  }
706
1123
 
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
1124
  class FormBuilderSettingsHost {
952
1125
  field = input(null, ...(ngDevMode ? [{ debugName: "field" }] : /* istanbul ignore next */ []));
953
1126
  section = input(null, ...(ngDevMode ? [{ debugName: "section" }] : /* istanbul ignore next */ []));
@@ -964,10 +1137,29 @@ class FormBuilderSettingsHost {
964
1137
  }
965
1138
  return field ? this.definitions().find(definition => definition.type === field.type) : undefined;
966
1139
  }, ...(ngDevMode ? [{ debugName: "itemDefinition" }] : /* istanbul ignore next */ []));
967
- fieldDefinition = this.itemDefinition;
968
- fieldKind = computed(() => this.itemDefinition()?.kind ?? this.field()?.kind ?? 'field', ...(ngDevMode ? [{ debugName: "fieldKind" }] : /* istanbul ignore next */ []));
969
- isLayoutField = computed(() => this.fieldKind() === 'layout' || !!this.field()?.children?.length, ...(ngDevMode ? [{ debugName: "isLayoutField" }] : /* istanbul ignore next */ []));
970
- customLoaded = signal(false, ...(ngDevMode ? [{ debugName: "customLoaded" }] : /* istanbul ignore next */ []));
1140
+ settingsSchema = computed(() => {
1141
+ const definition = this.itemDefinition();
1142
+ const config = this.settingsConfig(definition);
1143
+ const inheritance = config?.extends ?? this.inferSettingsInheritance(definition);
1144
+ const schemas = [
1145
+ this.baseSettingsSchema(inheritance),
1146
+ this.resolveOwnSettingsSchema(config, definition)
1147
+ ].filter((schema) => !!schema);
1148
+ return mergeSettingsSchemas(schemas);
1149
+ }, ...(ngDevMode ? [{ debugName: "settingsSchema" }] : /* istanbul ignore next */ []));
1150
+ settingsValue = computed(() => {
1151
+ const item = this.section() ?? this.field();
1152
+ const schema = this.settingsSchema();
1153
+ const value = {};
1154
+ if (!item || !schema) {
1155
+ return value;
1156
+ }
1157
+ for (const settingField of flattenSettingsFields(schema)) {
1158
+ value[settingField.name] = this.readSettingValue(item, settingField);
1159
+ }
1160
+ return value;
1161
+ }, ...(ngDevMode ? [{ debugName: "settingsValue" }] : /* istanbul ignore next */ []));
1162
+ legacyLoaded = signal(false, ...(ngDevMode ? [{ debugName: "legacyLoaded" }] : /* istanbul ignore next */ []));
971
1163
  anchor = viewChild.required('anchor', { read: ViewContainerRef });
972
1164
  constructor() {
973
1165
  effect(async () => {
@@ -979,18 +1171,18 @@ class FormBuilderSettingsHost {
979
1171
  const type = section ? 'section' : field?.type;
980
1172
  const itemDefinition = type ? definitions.find(definition => definition.type === type) : undefined;
981
1173
  const kind = section ? 'layout' : (itemDefinition?.kind ?? field?.kind ?? 'field');
982
- const customSettings = itemDefinition?.settings ?? settingDefinitions.find(definition => {
1174
+ const legacySettings = this.legacySettingsImporter(itemDefinition) ?? settingDefinitions.find(definition => {
983
1175
  if (type && (definition.itemType === type || definition.fieldType === type || definition.type === type)) {
984
1176
  return true;
985
1177
  }
986
1178
  return !!definition.kind && definition.kind === kind;
987
1179
  })?.component;
988
1180
  viewContainer.clear();
989
- this.customLoaded.set(false);
990
- if (!customSettings) {
1181
+ this.legacyLoaded.set(false);
1182
+ if (!legacySettings) {
991
1183
  return;
992
1184
  }
993
- const componentType = await customSettings();
1185
+ const componentType = await legacySettings();
994
1186
  const componentRef = viewContainer.createComponent(componentType);
995
1187
  componentRef.setInput('item', section ?? field);
996
1188
  componentRef.setInput('field', field);
@@ -1000,23 +1192,286 @@ class FormBuilderSettingsHost {
1000
1192
  componentRef.setInput('update', section ? this.updateSection() : this.update());
1001
1193
  componentRef.setInput('updateField', this.update());
1002
1194
  componentRef.setInput('updateSection', this.updateSection());
1003
- this.customLoaded.set(true);
1195
+ this.legacyLoaded.set(true);
1196
+ });
1197
+ }
1198
+ applySettingsValue(value) {
1199
+ const schema = this.settingsSchema();
1200
+ const section = this.section();
1201
+ const field = this.field();
1202
+ if (!schema) {
1203
+ return;
1204
+ }
1205
+ if (section) {
1206
+ const patch = this.createSettingsPatch(section, schema, value);
1207
+ if (Object.keys(patch).length) {
1208
+ this.updateSection()?.(patch);
1209
+ }
1210
+ return;
1211
+ }
1212
+ if (field) {
1213
+ const patch = this.createSettingsPatch(field, schema, value);
1214
+ if (Object.keys(patch).length) {
1215
+ this.update()?.(patch);
1216
+ }
1217
+ }
1218
+ }
1219
+ createSettingsPatch(item, schema, value) {
1220
+ const patch = {};
1221
+ for (const settingField of flattenSettingsFields(schema)) {
1222
+ if (settingField.settings?.['valueAdapter'] === 'optionsText' && isFormBuilderField(item)) {
1223
+ if (value[settingField.name] === this.readSettingValue(item, settingField)) {
1224
+ continue;
1225
+ }
1226
+ const nextOptions = parseOptionsText(String(value[settingField.name] ?? ''), item);
1227
+ patch['options'] = nextOptions;
1228
+ patch['defaultValue'] = resolveSelectedDefaultValue(nextOptions, item);
1229
+ continue;
1230
+ }
1231
+ if (settingField.settings?.['valueAdapter'] === 'selectMultiple' && isFormBuilderField(item)) {
1232
+ const multiple = value[settingField.name] === true;
1233
+ if (multiple === (item.multiple === true)) {
1234
+ continue;
1235
+ }
1236
+ const options = multiple ? item.options ?? [] : normalizeSelectedOptions(item.options ?? [], { ...item, multiple });
1237
+ patch['multiple'] = multiple;
1238
+ patch['options'] = options;
1239
+ patch['defaultValue'] = resolveMultipleDefaultValue(item, options, multiple);
1240
+ continue;
1241
+ }
1242
+ if (settingField.settings?.['valueAdapter'] === 'multipleDefaultValue' && isFormBuilderField(item)) {
1243
+ const multiple = value[settingField.name] === true;
1244
+ if (multiple === (item.multiple === true)) {
1245
+ continue;
1246
+ }
1247
+ patch['multiple'] = multiple;
1248
+ patch['defaultValue'] = multiple ? [] : null;
1249
+ continue;
1250
+ }
1251
+ const nextValue = value[settingField.name];
1252
+ if (nextValue === this.readSettingValue(item, settingField)) {
1253
+ continue;
1254
+ }
1255
+ seedPathPatch(patch, item, settingField.name);
1256
+ setPathValue(patch, settingField.name, nextValue);
1257
+ }
1258
+ return patch;
1259
+ }
1260
+ readSettingValue(item, settingField) {
1261
+ if (settingField.settings?.['valueAdapter'] === 'optionsText' && isFormBuilderField(item)) {
1262
+ return optionsToText(item.options ?? [], item.defaultValue);
1263
+ }
1264
+ const value = getPathValue(item, settingField.name);
1265
+ return value === undefined ? settingField.defaultValue ?? null : value;
1266
+ }
1267
+ settingsConfig(definition) {
1268
+ return definition?.settings && typeof definition.settings !== 'function'
1269
+ ? definition.settings
1270
+ : undefined;
1271
+ }
1272
+ legacySettingsImporter(definition) {
1273
+ return typeof definition?.settings === 'function'
1274
+ ? definition.settings
1275
+ : undefined;
1276
+ }
1277
+ resolveOwnSettingsSchema(config, definition) {
1278
+ const schema = config?.schema;
1279
+ if (!schema) {
1280
+ return null;
1281
+ }
1282
+ if (typeof schema !== 'function') {
1283
+ return schema;
1284
+ }
1285
+ const section = this.section();
1286
+ if (section) {
1287
+ return schema({
1288
+ item: section,
1289
+ section,
1290
+ schema: this.schema(),
1291
+ definition,
1292
+ update: this.updateSection(),
1293
+ updateSection: this.updateSection()
1294
+ });
1295
+ }
1296
+ const field = this.field();
1297
+ return schema({
1298
+ item: field,
1299
+ field,
1300
+ schema: this.schema(),
1301
+ definition,
1302
+ update: this.update(),
1303
+ updateField: this.update()
1004
1304
  });
1005
1305
  }
1006
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: FormBuilderSettingsHost, deps: [], target: i0.ɵɵFactoryTarget.Component });
1007
- 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": "!customLoaded()" }, 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 (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", dependencies: [{ kind: "component", type: BasicFormBuilderFieldSettings, selector: "ngs-basic-form-builder-field-settings", inputs: ["field", "update"], exportAs: ["ngsBasicFormBuilderFieldSettings"] }, { kind: "component", type: BasicFormBuilderLayoutSettings, selector: "ngs-basic-form-builder-layout-settings", inputs: ["field", "update"], exportAs: ["ngsBasicFormBuilderLayoutSettings"] }, { kind: "component", type: BasicFormBuilderSectionSettings, selector: "ngs-basic-form-builder-section-settings", inputs: ["section", "update"], exportAs: ["ngsBasicFormBuilderSectionSettings"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
1306
+ inferSettingsInheritance(definition) {
1307
+ const field = this.field();
1308
+ if (this.section()) {
1309
+ return 'none';
1310
+ }
1311
+ const kind = definition?.kind ?? field?.kind ?? 'field';
1312
+ if (kind === 'static') {
1313
+ return 'static';
1314
+ }
1315
+ if (kind === 'layout' || definition?.acceptsChildren || field?.children?.length) {
1316
+ return 'layout';
1317
+ }
1318
+ return 'field';
1319
+ }
1320
+ baseSettingsSchema(inheritance) {
1321
+ if (this.section()) {
1322
+ return inheritance === 'none' ? null : FORM_BUILDER_SECTION_BASE_SETTINGS_SCHEMA;
1323
+ }
1324
+ switch (inheritance) {
1325
+ case 'field':
1326
+ return FORM_BUILDER_FIELD_BASE_SETTINGS_SCHEMA;
1327
+ case 'input-field':
1328
+ return FORM_BUILDER_INPUT_FIELD_BASE_SETTINGS_SCHEMA;
1329
+ case 'layout':
1330
+ return FORM_BUILDER_LAYOUT_BASE_SETTINGS_SCHEMA;
1331
+ case 'layout-container':
1332
+ return FORM_BUILDER_LAYOUT_CONTAINER_BASE_SETTINGS_SCHEMA;
1333
+ case 'static':
1334
+ return FORM_BUILDER_STATIC_BASE_SETTINGS_SCHEMA;
1335
+ default:
1336
+ return null;
1337
+ }
1338
+ }
1339
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: FormBuilderSettingsHost, deps: [], target: i0.ɵɵFactoryTarget.Component });
1340
+ 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 });
1341
+ }
1342
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: FormBuilderSettingsHost, decorators: [{
1343
+ type: Component,
1344
+ args: [{ selector: 'ngs-form-builder-settings-host', exportAs: 'ngsFormBuilderSettingsHost', imports: [
1345
+ FormBuilderRenderer
1346
+ ], changeDetection: ChangeDetectionStrategy.OnPush, host: {
1347
+ 'class': 'ngs-form-builder-settings-host',
1348
+ '[class.is-empty]': '!settingsSchema() && !legacyLoaded()'
1349
+ }, 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" }]
1350
+ }], 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 }] }] } });
1351
+ function mergeSettingsSchemas(schemas) {
1352
+ if (!schemas.length) {
1353
+ return null;
1354
+ }
1355
+ return {
1356
+ sections: schemas.flatMap(schema => schema.sections.map(section => ({
1357
+ ...section,
1358
+ fields: section.fields.map(field => ({ ...field }))
1359
+ })))
1360
+ };
1361
+ }
1362
+ function flattenSettingsFields(schema) {
1363
+ return [
1364
+ ...(schema.fields ?? []),
1365
+ ...schema.sections.flatMap(section => section.fields)
1366
+ ].flatMap(field => [field, ...flattenSettingsFields({ sections: [], fields: field.children ?? [] })]);
1367
+ }
1368
+ function getPathValue(source, path) {
1369
+ return path.split('.').reduce((value, key) => value?.[key], source);
1370
+ }
1371
+ function setPathValue(target, path, value) {
1372
+ const keys = path.split('.');
1373
+ let cursor = target;
1374
+ keys.slice(0, -1).forEach(key => {
1375
+ cursor[key] = cursor[key] ?? {};
1376
+ cursor = cursor[key];
1377
+ });
1378
+ cursor[keys[keys.length - 1]] = value;
1379
+ }
1380
+ function seedPathPatch(target, source, path) {
1381
+ const [rootKey] = path.split('.');
1382
+ const sourceValue = source[rootKey];
1383
+ if (!path.includes('.') || target[rootKey] !== undefined || !isPlainObject(sourceValue)) {
1384
+ return;
1385
+ }
1386
+ target[rootKey] = { ...sourceValue };
1387
+ }
1388
+ function isPlainObject(value) {
1389
+ return typeof value === 'object' && value !== null && !Array.isArray(value);
1390
+ }
1391
+ function isFormBuilderField(item) {
1392
+ return 'type' in item;
1393
+ }
1394
+ function optionsToText(options, defaultValue) {
1395
+ return options
1396
+ .map(option => `${option.label}:${option.value}${isOptionSelected(option, defaultValue) ? ':selected' : ''}`)
1397
+ .join('\n');
1398
+ }
1399
+ function parseOptionsText(value, field) {
1400
+ return normalizeSelectedOptions(value
1401
+ .split('\n')
1402
+ .map(line => line.trim())
1403
+ .filter(Boolean)
1404
+ .map((line, index) => {
1405
+ const parsed = parseOptionLine$1(line);
1406
+ return {
1407
+ label: parsed?.label || `Option ${index + 1}`,
1408
+ value: parsed?.value || `option_${index + 1}`,
1409
+ selected: parsed?.selected || undefined
1410
+ };
1411
+ }), field);
1412
+ }
1413
+ function parseOptionLine$1(line) {
1414
+ const parts = line.split(':').map(part => part.trim());
1415
+ if (parts.length < 2 || !parts[0] || !parts[1]) {
1416
+ return null;
1417
+ }
1418
+ return {
1419
+ label: parts[0],
1420
+ value: parts[1],
1421
+ selected: parts[2] === 'selected'
1422
+ };
1423
+ }
1424
+ function normalizeSelectedOptions(options, field) {
1425
+ if (field.type === 'checkbox-list' || field.multiple) {
1426
+ return options;
1427
+ }
1428
+ let selectedSeen = false;
1429
+ return options.map(option => {
1430
+ if (!option.selected) {
1431
+ return option;
1432
+ }
1433
+ if (selectedSeen) {
1434
+ return {
1435
+ ...option,
1436
+ selected: undefined
1437
+ };
1438
+ }
1439
+ selectedSeen = true;
1440
+ return option;
1441
+ });
1442
+ }
1443
+ function resolveSelectedDefaultValue(options, field) {
1444
+ const selectedValues = options
1445
+ .filter(option => option.selected)
1446
+ .map(option => option.value);
1447
+ if (field.type === 'checkbox-list' || field.multiple) {
1448
+ return selectedValues;
1449
+ }
1450
+ return selectedValues[0] ?? null;
1451
+ }
1452
+ function resolveMultipleDefaultValue(field, options, multiple) {
1453
+ const selectedValues = options
1454
+ .filter(option => option.selected)
1455
+ .map(option => option.value);
1456
+ if (multiple) {
1457
+ return Array.isArray(field.defaultValue)
1458
+ ? field.defaultValue
1459
+ : field.defaultValue == null
1460
+ ? selectedValues
1461
+ : [field.defaultValue];
1462
+ }
1463
+ return Array.isArray(field.defaultValue)
1464
+ ? field.defaultValue[0] ?? selectedValues[0] ?? null
1465
+ : field.defaultValue ?? selectedValues[0] ?? null;
1466
+ }
1467
+ function isOptionSelected(option, defaultValue) {
1468
+ if (option.selected) {
1469
+ return true;
1470
+ }
1471
+ return Array.isArray(defaultValue)
1472
+ ? defaultValue.includes(option.value)
1473
+ : defaultValue === option.value;
1008
1474
  }
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
1475
 
1021
1476
  const ROOT_DROP_LIST_ID = 'ngs-form-builder-root-fields';
1022
1477
  const PALETTE_DRAG_TYPE = 'application/x-ngstarter-form-builder-field';
@@ -1123,7 +1578,7 @@ class FormBuilder {
1123
1578
  return this.fieldTreeRootNodes;
1124
1579
  }, ...(ngDevMode ? [{ debugName: "fieldTree" }] : /* istanbul ignore next */ []));
1125
1580
  fieldTreeChildrenAccessor = (node) => node.children ?? [];
1126
- hasFieldTreeChildren = (_, node) => !!node.children?.length;
1581
+ hasFieldTreeChildren = (_, node) => !!node.children;
1127
1582
  trackFieldTreeNode = (_, node) => node.id;
1128
1583
  fieldTreeDraggablePredicate = (_node) => true;
1129
1584
  fieldTreeDropPredicate = (source, target, position) => this.canDropFieldTreeNode(source, target, position);
@@ -1341,7 +1796,7 @@ class FormBuilder {
1341
1796
  }
1342
1797
  toggleFieldTreeNode(tree, node, event) {
1343
1798
  event.stopPropagation();
1344
- if (!node.children?.length) {
1799
+ if (!node.children) {
1345
1800
  return;
1346
1801
  }
1347
1802
  if (tree.isExpanded(node)) {
@@ -1679,11 +2134,6 @@ class FormBuilder {
1679
2134
  kind,
1680
2135
  label: baseLabel,
1681
2136
  width: definition.defaults?.width ?? this.defaultWidth(definition.type),
1682
- visibility: {
1683
- form: true,
1684
- email: true,
1685
- pdf: true
1686
- },
1687
2137
  ...definition.defaults
1688
2138
  };
1689
2139
  if (field.defaultValue === undefined) {
@@ -1875,7 +2325,7 @@ class FormBuilder {
1875
2325
  return;
1876
2326
  }
1877
2327
  for (const node of this.flattenFieldTree(this.fieldTree())) {
1878
- if (node.children?.length && expandedIds.has(node.id)) {
2328
+ if (node.children && expandedIds.has(node.id)) {
1879
2329
  tree.expand(node);
1880
2330
  }
1881
2331
  }
@@ -1899,7 +2349,7 @@ class FormBuilder {
1899
2349
  }
1900
2350
  const expandableIds = path
1901
2351
  .slice(0, -1)
1902
- .filter(node => !!node.children?.length)
2352
+ .filter(node => !!node.children)
1903
2353
  .map(node => node.id);
1904
2354
  if (expandableIds.length) {
1905
2355
  this.expandedFieldTreeNodeIds.update(ids => {
@@ -1915,7 +2365,7 @@ class FormBuilder {
1915
2365
  const tree = this.actualFieldsTree();
1916
2366
  if (tree) {
1917
2367
  for (const node of path.slice(0, -1)) {
1918
- if (node.children?.length) {
2368
+ if (node.children) {
1919
2369
  tree.expand(node);
1920
2370
  }
1921
2371
  }
@@ -2075,9 +2525,9 @@ class FormBuilder {
2075
2525
  node.kind = 'field';
2076
2526
  node.field = field;
2077
2527
  node.section = section;
2078
- if (children?.length) {
2528
+ if (children?.length || this.isContainerField(field)) {
2079
2529
  node.children ??= [];
2080
- replaceArrayContents(node.children, children);
2530
+ replaceArrayContents(node.children, children ?? []);
2081
2531
  }
2082
2532
  else {
2083
2533
  node.children = undefined;
@@ -2238,7 +2688,6 @@ function cloneField(field) {
2238
2688
  ...field,
2239
2689
  options: field.options?.map(option => ({ ...option })),
2240
2690
  validation: field.validation?.map(rule => ({ ...rule })),
2241
- visibility: field.visibility ? { ...field.visibility } : undefined,
2242
2691
  settings: field.settings ? { ...field.settings } : undefined,
2243
2692
  children: field.children?.map(cloneField)
2244
2693
  };
@@ -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