@ngstarter-ui/components 21.0.46 → 21.0.47

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