@ngstarter-ui/components 21.0.41 → 21.0.43

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.
Files changed (42) hide show
  1. package/README.md +0 -1
  2. package/ai/component-registry.json +67 -111
  3. package/fesm2022/ngstarter-ui-components-country-select.mjs +59 -16
  4. package/fesm2022/ngstarter-ui-components-country-select.mjs.map +1 -1
  5. package/fesm2022/ngstarter-ui-components-form-builder.mjs +432 -34
  6. package/fesm2022/ngstarter-ui-components-form-builder.mjs.map +1 -1
  7. package/fesm2022/ngstarter-ui-components-radio.mjs +8 -5
  8. package/fesm2022/ngstarter-ui-components-radio.mjs.map +1 -1
  9. package/fesm2022/ngstarter-ui-components-slide-toggle.mjs +12 -1
  10. package/fesm2022/ngstarter-ui-components-slide-toggle.mjs.map +1 -1
  11. package/package.json +1 -5
  12. package/types/ngstarter-ui-components-country-select.d.ts +20 -9
  13. package/types/ngstarter-ui-components-form-builder.d.ts +63 -5
  14. package/types/ngstarter-ui-components-radio.d.ts +4 -1
  15. package/types/ngstarter-ui-components-slide-toggle.d.ts +6 -1
  16. package/fesm2022/ngstarter-ui-components-form-renderer-autocomplete-many-field-CGQ3pJaG.mjs +0 -124
  17. package/fesm2022/ngstarter-ui-components-form-renderer-autocomplete-many-field-CGQ3pJaG.mjs.map +0 -1
  18. package/fesm2022/ngstarter-ui-components-form-renderer-checkbox-field-CoyKdvhV.mjs +0 -22
  19. package/fesm2022/ngstarter-ui-components-form-renderer-checkbox-field-CoyKdvhV.mjs.map +0 -1
  20. package/fesm2022/ngstarter-ui-components-form-renderer-datepicker-field-Cf3jfPv7.mjs +0 -44
  21. package/fesm2022/ngstarter-ui-components-form-renderer-datepicker-field-Cf3jfPv7.mjs.map +0 -1
  22. package/fesm2022/ngstarter-ui-components-form-renderer-divider-content-D8Sffofu.mjs +0 -17
  23. package/fesm2022/ngstarter-ui-components-form-renderer-divider-content-D8Sffofu.mjs.map +0 -1
  24. package/fesm2022/ngstarter-ui-components-form-renderer-image-content-ICTwkZPa.mjs +0 -17
  25. package/fesm2022/ngstarter-ui-components-form-renderer-image-content-ICTwkZPa.mjs.map +0 -1
  26. package/fesm2022/ngstarter-ui-components-form-renderer-input-field-tZHU8-_L.mjs +0 -35
  27. package/fesm2022/ngstarter-ui-components-form-renderer-input-field-tZHU8-_L.mjs.map +0 -1
  28. package/fesm2022/ngstarter-ui-components-form-renderer-radio-group-field-lc1V-NeL.mjs +0 -38
  29. package/fesm2022/ngstarter-ui-components-form-renderer-radio-group-field-lc1V-NeL.mjs.map +0 -1
  30. package/fesm2022/ngstarter-ui-components-form-renderer-select-field-DyBidcIU.mjs +0 -39
  31. package/fesm2022/ngstarter-ui-components-form-renderer-select-field-DyBidcIU.mjs.map +0 -1
  32. package/fesm2022/ngstarter-ui-components-form-renderer-text-content-BjzH_M3-.mjs +0 -24
  33. package/fesm2022/ngstarter-ui-components-form-renderer-text-content-BjzH_M3-.mjs.map +0 -1
  34. package/fesm2022/ngstarter-ui-components-form-renderer-textarea-field-DxbPsw3z.mjs +0 -37
  35. package/fesm2022/ngstarter-ui-components-form-renderer-textarea-field-DxbPsw3z.mjs.map +0 -1
  36. package/fesm2022/ngstarter-ui-components-form-renderer-timezone-field-DAWrM-jS.mjs +0 -35
  37. package/fesm2022/ngstarter-ui-components-form-renderer-timezone-field-DAWrM-jS.mjs.map +0 -1
  38. package/fesm2022/ngstarter-ui-components-form-renderer-toggle-field-iyqUrWxt.mjs +0 -22
  39. package/fesm2022/ngstarter-ui-components-form-renderer-toggle-field-iyqUrWxt.mjs.map +0 -1
  40. package/fesm2022/ngstarter-ui-components-form-renderer.mjs +0 -317
  41. package/fesm2022/ngstarter-ui-components-form-renderer.mjs.map +0 -1
  42. package/types/ngstarter-ui-components-form-renderer.d.ts +0 -121
@@ -1,5 +1,5 @@
1
1
  import * as i0 from '@angular/core';
2
- import { InjectionToken, makeEnvironmentProviders, input, signal, computed, viewChild, ViewContainerRef, effect, ChangeDetectionStrategy, Component, inject, model, output, ElementRef } from '@angular/core';
2
+ import { InjectionToken, makeEnvironmentProviders, inject, input, signal, computed, viewChild, ViewContainerRef, effect, ChangeDetectionStrategy, Component, model, output, DestroyRef, ElementRef } from '@angular/core';
3
3
  import * as i1 from '@angular/forms';
4
4
  import { Validators, ReactiveFormsModule, FormControl, FormGroup, FormsModule } from '@angular/forms';
5
5
  import { NgTemplateOutlet } from '@angular/common';
@@ -13,16 +13,24 @@ import { Panel, PanelAside, PanelContent, PanelHeader, PanelSidebar } from '@ngs
13
13
  import { ScrollbarArea } from '@ngstarter-ui/components/scrollbar-area';
14
14
  import { Tab, TabGroup } from '@ngstarter-ui/components/tabs';
15
15
  import { Tree, TreeDragPlaceholder, TreeNode, TreeNodeDef, TreeNodePadding } from '@ngstarter-ui/components/tree';
16
- import { Datepicker, DatepickerInput, DatepickerToggle, provideNativeDateAdapter } from '@ngstarter-ui/components/datepicker';
17
- import { FormField, Hint, IconButtonSuffix, Label } from '@ngstarter-ui/components/form-field';
16
+ import { DateRange, Datepicker, DatepickerInput, DatepickerToggle, DateRangeInput, DateRangePicker, StartDate, EndDate, provideNativeDateAdapter } from '@ngstarter-ui/components/datepicker';
17
+ import { FormField, Hint, IconButtonSuffix, Label, Error } from '@ngstarter-ui/components/form-field';
18
18
  import { Select } from '@ngstarter-ui/components/select';
19
19
  import { Option } from '@ngstarter-ui/components/option';
20
20
  import { Checkbox } from '@ngstarter-ui/components/checkbox';
21
- import { SlideToggle } from '@ngstarter-ui/components/slide-toggle';
21
+ import { SlideToggle, SlideToggleGroup } from '@ngstarter-ui/components/slide-toggle';
22
+ import { RadioButton, RadioGroup } from '@ngstarter-ui/components/radio';
23
+ import { CountrySelect } from '@ngstarter-ui/components/country-select';
24
+ import { CurrencySelect } from '@ngstarter-ui/components/currency-select';
25
+ import { TimezoneSelect } from '@ngstarter-ui/components/timezone-select';
26
+ import { Timepicker, TimepickerInput, TimepickerToggle } from '@ngstarter-ui/components/timepicker';
27
+ import { UploadArea, UploadAreaIconDirective, UploadAreaMainStateDirective, UploadAreaDropStateDirective, UploadAreaInvalidStateDirective, UploadTriggerDirective } from '@ngstarter-ui/components/upload';
28
+ import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
22
29
 
23
30
  const FORM_BUILDER_ITEMS = new InjectionToken('FORM_BUILDER_ITEMS');
24
31
  const FORM_BUILDER_FIELDS = new InjectionToken('FORM_BUILDER_FIELDS');
25
32
  const FORM_BUILDER_SETTINGS = new InjectionToken('FORM_BUILDER_SETTINGS');
33
+ const FORM_BUILDER_UPLOAD_CALLBACK = new InjectionToken('FORM_BUILDER_UPLOAD_CALLBACK');
26
34
  function formBuilderField(definition) {
27
35
  return definition;
28
36
  }
@@ -34,6 +42,10 @@ function formBuilderSettings(definition) {
34
42
  }
35
43
  function provideFormBuilder(config = {}) {
36
44
  return makeEnvironmentProviders([
45
+ ...(config.uploadCallback ? [{
46
+ provide: FORM_BUILDER_UPLOAD_CALLBACK,
47
+ useValue: config.uploadCallback
48
+ }] : []),
37
49
  ...(config.items ?? []).map(item => ({
38
50
  provide: FORM_BUILDER_ITEMS,
39
51
  useValue: item,
@@ -109,6 +121,22 @@ const DEFAULT_FORM_BUILDER_FIELDS = [
109
121
  children: []
110
122
  }
111
123
  },
124
+ {
125
+ type: 'spacer',
126
+ label: 'Spacer',
127
+ kind: 'static',
128
+ group: 'Layout',
129
+ icon: 'fluent:resize-large-24-regular',
130
+ description: 'Static vertical space between form elements.',
131
+ defaults: {
132
+ kind: 'static',
133
+ label: 'Spacer',
134
+ width: 12,
135
+ settings: {
136
+ height: 24
137
+ }
138
+ }
139
+ },
112
140
  {
113
141
  type: 'select',
114
142
  label: 'Select',
@@ -117,6 +145,23 @@ const DEFAULT_FORM_BUILDER_FIELDS = [
117
145
  defaults: {
118
146
  label: 'Select',
119
147
  placeholder: 'Choose an option',
148
+ clearable: true,
149
+ options: [
150
+ { label: 'Option 1', value: 'option_1' },
151
+ { label: 'Option 2', value: 'option_2' }
152
+ ]
153
+ }
154
+ },
155
+ {
156
+ type: 'radio',
157
+ label: 'Radio',
158
+ group: 'Choices',
159
+ icon: 'fluent:radio-button-24-regular',
160
+ defaults: {
161
+ label: 'Radio',
162
+ settings: {
163
+ orientation: 'vertical'
164
+ },
120
165
  options: [
121
166
  { label: 'Option 1', value: 'option_1' },
122
167
  { label: 'Option 2', value: 'option_2' }
@@ -153,6 +198,52 @@ const DEFAULT_FORM_BUILDER_FIELDS = [
153
198
  placeholder: 'Select date'
154
199
  }
155
200
  },
201
+ {
202
+ type: 'time',
203
+ label: 'Time',
204
+ group: 'Date and time',
205
+ icon: 'fluent:clock-24-regular',
206
+ defaults: {
207
+ label: 'Time',
208
+ placeholder: 'Select time'
209
+ }
210
+ },
211
+ {
212
+ type: 'date-range',
213
+ label: 'Date range',
214
+ group: 'Date and time',
215
+ icon: 'fluent:calendar-date-24-regular',
216
+ defaults: {
217
+ label: 'Date range',
218
+ placeholder: 'Start date',
219
+ width: 12
220
+ }
221
+ },
222
+ {
223
+ type: 'upload',
224
+ label: 'Upload',
225
+ group: 'Files',
226
+ icon: 'fluent:arrow-upload-24-regular',
227
+ defaults: {
228
+ label: 'Upload',
229
+ placeholder: 'Drop files or click to upload',
230
+ width: 12,
231
+ multiple: false,
232
+ settings: {
233
+ accept: '*/*'
234
+ }
235
+ }
236
+ },
237
+ {
238
+ type: 'timezone-select',
239
+ label: 'Timezone select',
240
+ group: 'Date and time',
241
+ icon: 'fluent:globe-clock-24-regular',
242
+ defaults: {
243
+ label: 'Timezone',
244
+ placeholder: 'Select timezone'
245
+ }
246
+ },
156
247
  {
157
248
  type: 'currency',
158
249
  label: 'Currency',
@@ -162,6 +253,27 @@ const DEFAULT_FORM_BUILDER_FIELDS = [
162
253
  label: 'Amount',
163
254
  placeholder: '0.00'
164
255
  }
256
+ },
257
+ {
258
+ type: 'currency-select',
259
+ label: 'Currency select',
260
+ group: 'Finance',
261
+ icon: 'fluent:money-hand-24-regular',
262
+ defaults: {
263
+ label: 'Currency',
264
+ placeholder: 'Select currency'
265
+ }
266
+ },
267
+ {
268
+ type: 'country-select',
269
+ label: 'Country select',
270
+ group: 'Location',
271
+ icon: 'fluent:globe-location-24-regular',
272
+ defaults: {
273
+ label: 'Country',
274
+ placeholder: 'Select country',
275
+ clearable: true
276
+ }
165
277
  }
166
278
  ];
167
279
  const DEFAULT_FORM_BUILDER_ITEMS = [
@@ -202,18 +314,54 @@ function validatorsFromRules(rules = [], field) {
202
314
  }
203
315
 
204
316
  class FormBuilderFieldHost {
317
+ providedUploadCallback = inject(FORM_BUILDER_UPLOAD_CALLBACK, { optional: true });
205
318
  field = input.required(...(ngDevMode ? [{ debugName: "field" }] : /* istanbul ignore next */ []));
206
319
  control = input.required(...(ngDevMode ? [{ debugName: "control" }] : /* istanbul ignore next */ []));
207
320
  definitions = input([], ...(ngDevMode ? [{ debugName: "definitions" }] : /* istanbul ignore next */ []));
208
321
  readonly = input(false, ...(ngDevMode ? [{ debugName: "readonly" }] : /* istanbul ignore next */ []));
209
322
  editableCanvas = input(false, ...(ngDevMode ? [{ debugName: "editableCanvas" }] : /* istanbul ignore next */ []));
323
+ uploadCallback = input(undefined, ...(ngDevMode ? [{ debugName: "uploadCallback" }] : /* istanbul ignore next */ []));
210
324
  customLoaded = signal(false, ...(ngDevMode ? [{ debugName: "customLoaded" }] : /* istanbul ignore next */ []));
325
+ controlValue = signal(null, ...(ngDevMode ? [{ debugName: "controlValue" }] : /* istanbul ignore next */ []));
211
326
  textInputType = computed(() => {
212
327
  const type = this.field().type;
213
328
  return type === 'number' || type === 'email' ? type : 'text';
214
329
  }, ...(ngDevMode ? [{ debugName: "textInputType" }] : /* istanbul ignore next */ []));
330
+ radioOrientation = computed(() => this.field().settings?.['orientation'] === 'horizontal' ? 'horizontal' : 'vertical', ...(ngDevMode ? [{ debugName: "radioOrientation" }] : /* istanbul ignore next */ []));
331
+ uploadAccept = computed(() => this.field().settings?.['accept'] || '*/*', ...(ngDevMode ? [{ debugName: "uploadAccept" }] : /* istanbul ignore next */ []));
332
+ uploadDisabled = computed(() => this.readonly() || this.field().readonly || this.field().disabled || this.control().disabled, ...(ngDevMode ? [{ debugName: "uploadDisabled" }] : /* istanbul ignore next */ []));
333
+ dateRangeDisabled = computed(() => this.readonly() || this.field().readonly || this.field().disabled || this.control().disabled, ...(ngDevMode ? [{ debugName: "dateRangeDisabled" }] : /* istanbul ignore next */ []));
334
+ uploadSelectedText = computed(() => {
335
+ const value = this.controlValue();
336
+ const files = Array.isArray(value)
337
+ ? value
338
+ : value
339
+ ? [value]
340
+ : [];
341
+ if (!files.length) {
342
+ return this.field().placeholder || 'Drop files or click to upload';
343
+ }
344
+ if (files.length === 1) {
345
+ return files[0]?.name || '1 file selected';
346
+ }
347
+ return `${files.length} files selected`;
348
+ }, ...(ngDevMode ? [{ debugName: "uploadSelectedText" }] : /* istanbul ignore next */ []));
349
+ spacerHeight = computed(() => {
350
+ const height = Number(this.field().settings?.['height'] ?? 24);
351
+ return [8, 16, 24, 32, 48, 64].includes(height) ? height : 24;
352
+ }, ...(ngDevMode ? [{ debugName: "spacerHeight" }] : /* istanbul ignore next */ []));
353
+ dateRangeStartValue = computed(() => this.formatDateRangePart(this.controlValue()?.start), ...(ngDevMode ? [{ debugName: "dateRangeStartValue" }] : /* istanbul ignore next */ []));
354
+ dateRangeEndValue = computed(() => this.formatDateRangePart(this.controlValue()?.end), ...(ngDevMode ? [{ debugName: "dateRangeEndValue" }] : /* istanbul ignore next */ []));
215
355
  anchor = viewChild.required('anchor', { read: ViewContainerRef });
216
356
  constructor() {
357
+ effect(onCleanup => {
358
+ const control = this.control();
359
+ this.controlValue.set(control.value);
360
+ const subscription = control.valueChanges.subscribe(value => {
361
+ this.controlValue.set(value);
362
+ });
363
+ onCleanup(() => subscription.unsubscribe());
364
+ });
217
365
  effect(async () => {
218
366
  const field = this.field();
219
367
  const control = this.control();
@@ -234,10 +382,56 @@ class FormBuilderFieldHost {
234
382
  this.customLoaded.set(true);
235
383
  });
236
384
  }
385
+ async onUploadFilesSelected(event) {
386
+ if (this.uploadDisabled()) {
387
+ return;
388
+ }
389
+ const fallbackValue = this.field().multiple
390
+ ? event.files
391
+ : event.files[0] ?? null;
392
+ const callback = this.uploadCallback() ?? this.providedUploadCallback;
393
+ let value = fallbackValue;
394
+ if (callback) {
395
+ this.control().markAsPending();
396
+ const uploadedValue = await callback({
397
+ field: this.field(),
398
+ control: this.control(),
399
+ event: event.event,
400
+ files: event.files,
401
+ fileList: event.fileList,
402
+ multiple: event.multiple
403
+ });
404
+ if (uploadedValue !== undefined) {
405
+ value = uploadedValue;
406
+ }
407
+ }
408
+ this.control().setValue(value);
409
+ this.control().markAsDirty();
410
+ this.control().markAsTouched();
411
+ this.control().updateValueAndValidity();
412
+ }
413
+ onDateRangeChanged(rangeInput) {
414
+ queueMicrotask(() => {
415
+ const range = rangeInput.value;
416
+ const nextRange = range ?? new DateRange(null, null);
417
+ this.control().setValue(nextRange);
418
+ this.control().markAsDirty();
419
+ this.control().markAsTouched();
420
+ });
421
+ }
422
+ formatDateRangePart(value) {
423
+ if (!value) {
424
+ return '';
425
+ }
426
+ if (value instanceof Date) {
427
+ return value.toLocaleDateString();
428
+ }
429
+ return String(value);
430
+ }
237
431
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: FormBuilderFieldHost, deps: [], target: i0.ɵɵFactoryTarget.Component });
238
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.4", type: FormBuilderFieldHost, isStandalone: true, selector: "ngs-form-builder-field-host", inputs: { field: { classPropertyName: "field", publicName: "field", isSignal: true, isRequired: true, transformFunction: null }, control: { classPropertyName: "control", publicName: "control", isSignal: true, isRequired: true, transformFunction: null }, definitions: { classPropertyName: "definitions", publicName: "definitions", isSignal: true, isRequired: false, transformFunction: null }, readonly: { classPropertyName: "readonly", publicName: "readonly", isSignal: true, isRequired: false, transformFunction: null }, editableCanvas: { classPropertyName: "editableCanvas", publicName: "editableCanvas", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "class.is-custom": "customLoaded()", "class.is-width-1": "!editableCanvas() && field().width === 1", "class.is-width-2": "!editableCanvas() && field().width === 2", "class.is-width-3": "!editableCanvas() && field().width === 3", "class.is-width-4": "!editableCanvas() && field().width === 4", "class.is-width-5": "!editableCanvas() && field().width === 5", "class.is-width-6": "!editableCanvas() && field().width === 6", "class.is-width-7": "!editableCanvas() && field().width === 7", "class.is-width-8": "!editableCanvas() && field().width === 8", "class.is-width-9": "!editableCanvas() && field().width === 9", "class.is-width-10": "!editableCanvas() && field().width === 10", "class.is-width-11": "!editableCanvas() && field().width === 11", "class.is-width-12": "!editableCanvas() && (field().width ?? 12) === 12" }, classAttribute: "ngs-form-builder-field-host" }, providers: [
432
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.4", type: FormBuilderFieldHost, isStandalone: true, selector: "ngs-form-builder-field-host", inputs: { field: { classPropertyName: "field", publicName: "field", isSignal: true, isRequired: true, transformFunction: null }, control: { classPropertyName: "control", publicName: "control", isSignal: true, isRequired: true, transformFunction: null }, definitions: { classPropertyName: "definitions", publicName: "definitions", isSignal: true, isRequired: false, transformFunction: null }, readonly: { classPropertyName: "readonly", publicName: "readonly", isSignal: true, isRequired: false, transformFunction: null }, editableCanvas: { classPropertyName: "editableCanvas", publicName: "editableCanvas", isSignal: true, isRequired: false, transformFunction: null }, uploadCallback: { classPropertyName: "uploadCallback", publicName: "uploadCallback", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "class.is-custom": "customLoaded()", "class.is-width-1": "!editableCanvas() && field().width === 1", "class.is-width-2": "!editableCanvas() && field().width === 2", "class.is-width-3": "!editableCanvas() && field().width === 3", "class.is-width-4": "!editableCanvas() && field().width === 4", "class.is-width-5": "!editableCanvas() && field().width === 5", "class.is-width-6": "!editableCanvas() && field().width === 6", "class.is-width-7": "!editableCanvas() && field().width === 7", "class.is-width-8": "!editableCanvas() && field().width === 8", "class.is-width-9": "!editableCanvas() && field().width === 9", "class.is-width-10": "!editableCanvas() && field().width === 10", "class.is-width-11": "!editableCanvas() && field().width === 11", "class.is-width-12": "!editableCanvas() && (field().width ?? 12) === 12" }, classAttribute: "ngs-form-builder-field-host" }, providers: [
239
433
  provideNativeDateAdapter()
240
- ], viewQueries: [{ propertyName: "anchor", first: true, predicate: ["anchor"], descendants: true, read: ViewContainerRef, isSignal: true }], exportAs: ["ngsFormBuilderFieldHost"], ngImport: i0, template: "<ng-container #anchor/>\n\n@if (!customLoaded()) {\n @switch (field().type) {\n @case ('textarea') {\n <ngs-form-field>\n <ngs-label>{{ field().label }}</ngs-label>\n <textarea ngsInput\n rows=\"3\"\n [formControl]=\"control()\"\n [placeholder]=\"field().placeholder || ''\"\n [readonly]=\"readonly() || field().readonly\"></textarea>\n @if (field().hint) {\n <ngs-hint>{{ field().hint }}</ngs-hint>\n }\n </ngs-form-field>\n }\n\n @case ('select') {\n <ngs-form-field>\n <ngs-label>{{ field().label }}</ngs-label>\n <ngs-select [formControl]=\"control()\" [placeholder]=\"field().placeholder || ''\">\n @for (option of field().options ?? []; track option.value) {\n <ngs-option [value]=\"option.value\">{{ option.label }}</ngs-option>\n }\n </ngs-select>\n @if (field().hint) {\n <ngs-hint>{{ field().hint }}</ngs-hint>\n }\n </ngs-form-field>\n }\n\n @case ('checkbox') {\n <div class=\"ngs-form-builder-check-field\">\n <ngs-checkbox [formControl]=\"control()\">\n {{ field().label }}\n </ngs-checkbox>\n @if (field().hint) {\n <span>{{ field().hint }}</span>\n }\n </div>\n }\n\n @case ('toggle') {\n <div class=\"ngs-form-builder-check-field\">\n <ngs-slide-toggle [formControl]=\"control()\">\n {{ field().label }}\n </ngs-slide-toggle>\n @if (field().hint) {\n <span>{{ field().hint }}</span>\n }\n </div>\n }\n\n @case ('date') {\n <ngs-form-field>\n <ngs-label>{{ field().label }}</ngs-label>\n <input ngsInput\n [ngsDatepicker]=\"picker\"\n [formControl]=\"control()\"\n [placeholder]=\"field().placeholder || ''\"\n [readonly]=\"readonly() || field().readonly\">\n <ngs-datepicker-toggle ngsIconButtonSuffix [for]=\"picker\"/>\n <ngs-datepicker #picker/>\n @if (field().hint) {\n <ngs-hint>{{ field().hint }}</ngs-hint>\n }\n </ngs-form-field>\n }\n\n @case ('currency') {\n <ngs-form-field>\n <ngs-label>{{ field().label }}</ngs-label>\n <input ngsInput\n type=\"number\"\n inputmode=\"decimal\"\n [formControl]=\"control()\"\n [placeholder]=\"field().placeholder || ''\"\n [readonly]=\"readonly() || field().readonly\">\n @if (field().hint) {\n <ngs-hint>{{ field().hint }}</ngs-hint>\n }\n </ngs-form-field>\n }\n\n @default {\n <ngs-form-field>\n <ngs-label>{{ field().label }}</ngs-label>\n <input ngsInput\n [type]=\"textInputType()\"\n [formControl]=\"control()\"\n [placeholder]=\"field().placeholder || ''\"\n [readonly]=\"readonly() || field().readonly\">\n @if (field().hint) {\n <ngs-hint>{{ field().hint }}</ngs-hint>\n }\n </ngs-form-field>\n }\n }\n}\n", styles: [":host{display:block;min-width:0}:host.is-width-1{grid-column:span 1}:host.is-width-2{grid-column:span 2}:host.is-width-3{grid-column:span 3}:host.is-width-4{grid-column:span 4}:host.is-width-5{grid-column:span 5}:host.is-width-6{grid-column:span 6}:host.is-width-7{grid-column:span 7}:host.is-width-8{grid-column:span 8}:host.is-width-9{grid-column:span 9}:host.is-width-10{grid-column:span 10}:host.is-width-11{grid-column:span 11}:host.is-width-12{grid-column:span 12}:host .ngs-form-builder-check-field{display:flex;min-height:calc(var(--spacing, .25rem) * 11);flex-direction:column;justify-content:center;gap:calc(var(--spacing, .25rem) * 1)}:host .ngs-form-builder-check-field span{color:var(--ngs-color-on-surface-variant);font-size:var(--ngs-font-size-xs)}\n/*! tailwindcss v4.2.2 | MIT License | https://tailwindcss.com */\n"], dependencies: [{ kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1.NumberValueAccessor, selector: "input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]" }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }, { kind: "component", type: Datepicker, selector: "ngs-datepicker", inputs: ["startAt", "calendarHeaderComponent", "quickPresets", "showQuickPresets"], exportAs: ["ngsDatepicker"] }, { kind: "directive", type: DatepickerInput, selector: "input[ngsDatepicker]", inputs: ["ngsDatepicker"], exportAs: ["ngsDatepickerInput"] }, { kind: "component", type: DatepickerToggle, selector: "ngs-datepicker-toggle", inputs: ["for"], exportAs: ["ngsDatepickerToggle"] }, { kind: "component", type: FormField, selector: "ngs-form-field", inputs: ["subscriptHiddenIfEmpty", "sameHeightAsButton"], exportAs: ["ngsFormField"] }, { kind: "component", type: Hint, selector: "ngs-hint", inputs: ["align"], exportAs: ["ngsHint"] }, { kind: "directive", type: IconButtonSuffix, selector: "[ngsIconButtonSuffix]" }, { kind: "component", type: Label, selector: "ngs-label" }, { kind: "directive", type: Input, selector: "input[ngsInput], textarea[ngsInput]", inputs: ["id", "placeholder", "required", "disabled", "readonly", "errorStateMatcher"], exportAs: ["ngsInput"] }, { kind: "component", type: Select, selector: "ngs-select", inputs: ["id", "placeholder", "disabled", "required", "multiple", "hideCheckIcon", "clearable", "aria-label", "tabIndex", "aria-describedby", "value"], outputs: ["selectionChange", "opened", "closed", "valueChange"], exportAs: ["ngsSelect"] }, { kind: "component", type: Option, selector: "ngs-option", inputs: ["value", "data", "disabled", "selected"], outputs: ["onSelectionChange"], exportAs: ["ngsOption"] }, { kind: "component", type: Checkbox, selector: "ngs-checkbox", inputs: ["aria-label", "aria-labelledby", "aria-describedby", "aria-expanded", "aria-controls", "aria-owns", "id", "required", "labelPosition", "name", "value", "disableRipple", "tabIndex", "color", "disabledInteractive", "checked", "disabled", "indeterminate"], outputs: ["checkedChange", "disabledChange", "indeterminateChange", "change"], exportAs: ["ngsCheckbox"] }, { kind: "component", type: SlideToggle, selector: "ngs-slide-toggle", inputs: ["id", "name", "labelPosition", "aria-label", "aria-labelledby", "aria-describedby", "required", "disabled", "disableRipple", "tabIndex", "hideIcon", "color", "checked"], outputs: ["disabledChange", "checkedChange", "change", "toggleChange"], exportAs: ["ngsSlideToggle"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
434
+ ], viewQueries: [{ propertyName: "anchor", first: true, predicate: ["anchor"], descendants: true, read: ViewContainerRef, isSignal: true }], exportAs: ["ngsFormBuilderFieldHost"], ngImport: i0, template: "<ng-container #anchor/>\n\n@if (!customLoaded()) {\n @switch (field().type) {\n @case ('textarea') {\n <ngs-form-field>\n <ngs-label>{{ field().label }}</ngs-label>\n <textarea ngsInput\n rows=\"3\"\n [formControl]=\"control()\"\n [placeholder]=\"field().placeholder || ''\"\n [readonly]=\"readonly() || field().readonly\"></textarea>\n @if (field().hint) {\n <ngs-hint>{{ field().hint }}</ngs-hint>\n }\n </ngs-form-field>\n }\n\n @case ('select') {\n <ngs-form-field>\n <ngs-label>{{ field().label }}</ngs-label>\n <ngs-select [formControl]=\"control()\"\n [placeholder]=\"field().placeholder || ''\"\n [multiple]=\"field().multiple ?? false\"\n [clearable]=\"field().clearable ?? false\">\n @for (option of field().options ?? []; track option.value) {\n <ngs-option [value]=\"option.value\">{{ option.label }}</ngs-option>\n }\n </ngs-select>\n @if (field().hint) {\n <ngs-hint>{{ field().hint }}</ngs-hint>\n }\n </ngs-form-field>\n }\n\n @case ('radio') {\n <div class=\"ngs-form-builder-choice-field\">\n <span class=\"ngs-form-builder-choice-label\">{{ field().label }}</span>\n <ngs-radio-group [formControl]=\"control()\"\n [name]=\"field().name\"\n [orientation]=\"radioOrientation()\">\n @for (option of field().options ?? []; track option.value) {\n <ngs-radio-button [value]=\"option.value\">{{ option.label }}</ngs-radio-button>\n }\n </ngs-radio-group>\n @if (field().hint) {\n <span class=\"ngs-form-builder-choice-hint\">{{ field().hint }}</span>\n }\n </div>\n }\n\n @case ('checkbox') {\n <div class=\"ngs-form-builder-check-field\">\n <ngs-checkbox [formControl]=\"control()\">\n {{ field().label }}\n </ngs-checkbox>\n @if (field().hint) {\n <span>{{ field().hint }}</span>\n }\n </div>\n }\n\n @case ('toggle') {\n <div class=\"ngs-form-builder-check-field\">\n <ngs-slide-toggle [formControl]=\"control()\">\n {{ field().label }}\n </ngs-slide-toggle>\n @if (field().hint) {\n <span>{{ field().hint }}</span>\n }\n </div>\n }\n\n @case ('date') {\n <ngs-form-field>\n <ngs-label>{{ field().label }}</ngs-label>\n <input ngsInput\n [ngsDatepicker]=\"picker\"\n [formControl]=\"control()\"\n [placeholder]=\"field().placeholder || ''\"\n [readonly]=\"readonly() || field().readonly\">\n <ngs-datepicker-toggle ngsIconButtonSuffix [for]=\"picker\"/>\n <ngs-datepicker #picker/>\n @if (field().hint) {\n <ngs-hint>{{ field().hint }}</ngs-hint>\n }\n </ngs-form-field>\n }\n\n @case ('time') {\n <ngs-form-field>\n <ngs-label>{{ field().label }}</ngs-label>\n <input ngsInput\n [ngsTimepicker]=\"timePicker\"\n [formControl]=\"control()\"\n [placeholder]=\"field().placeholder || ''\"\n [readonly]=\"readonly() || field().readonly\">\n <ngs-timepicker-toggle ngsIconButtonSuffix [for]=\"timePicker\"/>\n <ngs-timepicker #timePicker/>\n @if (field().hint) {\n <ngs-hint>{{ field().hint }}</ngs-hint>\n }\n </ngs-form-field>\n }\n\n @case ('date-range') {\n <ngs-form-field>\n <ngs-label>{{ field().label }}</ngs-label>\n <ngs-date-range-input #rangeInput [rangePicker]=\"rangePicker\">\n <input ngsStartDate\n [value]=\"dateRangeStartValue()\"\n [placeholder]=\"field().placeholder || 'Start date'\"\n [readonly]=\"readonly() || field().readonly\"\n [disabled]=\"dateRangeDisabled()\"\n (input)=\"onDateRangeChanged(rangeInput)\">\n <input ngsEndDate\n [value]=\"dateRangeEndValue()\"\n placeholder=\"End date\"\n [readonly]=\"readonly() || field().readonly\"\n [disabled]=\"dateRangeDisabled()\"\n (input)=\"onDateRangeChanged(rangeInput)\">\n </ngs-date-range-input>\n <ngs-datepicker-toggle ngsIconButtonSuffix [for]=\"rangePicker\"/>\n <ngs-date-range-picker #rangePicker/>\n @if (field().hint) {\n <ngs-hint>{{ field().hint }}</ngs-hint>\n }\n </ngs-form-field>\n }\n\n @case ('upload') {\n <div class=\"ngs-form-builder-upload-field\"\n [class.is-disabled]=\"uploadDisabled()\">\n <span class=\"ngs-form-builder-choice-label\">{{ field().label }}</span>\n <ngs-upload-area ngsUploadTrigger\n [accept]=\"uploadAccept()\"\n [multiple]=\"field().multiple ?? false\"\n (fileSelected)=\"onUploadFilesSelected($event)\">\n <ngs-icon name=\"fluent:arrow-upload-24-regular\" ngsUploadAreaIcon/>\n <ng-container ngsUploadAreaMainState>\n {{ uploadSelectedText() }}\n </ng-container>\n <ng-container ngsUploadAreaDropState>\n Drop files here\n </ng-container>\n <ng-container ngsUploadAreaInvalidState>\n Invalid file type\n </ng-container>\n </ngs-upload-area>\n @if (field().hint) {\n <span class=\"ngs-form-builder-choice-hint\">{{ field().hint }}</span>\n }\n </div>\n }\n\n @case ('spacer') {\n <div class=\"ngs-form-builder-spacer\"\n [class.is-height-8]=\"spacerHeight() === 8\"\n [class.is-height-16]=\"spacerHeight() === 16\"\n [class.is-height-24]=\"spacerHeight() === 24\"\n [class.is-height-32]=\"spacerHeight() === 32\"\n [class.is-height-48]=\"spacerHeight() === 48\"\n [class.is-height-64]=\"spacerHeight() === 64\"\n aria-hidden=\"true\"></div>\n }\n\n @case ('timezone-select') {\n <ngs-form-field>\n <ngs-label>{{ field().label }}</ngs-label>\n <ngs-timezone-select [formControl]=\"control()\"\n [placeholder]=\"field().placeholder || ''\"\n [required]=\"field().required ?? false\"/>\n @if (field().hint) {\n <ngs-hint>{{ field().hint }}</ngs-hint>\n }\n </ngs-form-field>\n }\n\n @case ('currency') {\n <ngs-form-field>\n <ngs-label>{{ field().label }}</ngs-label>\n <input ngsInput\n type=\"number\"\n inputmode=\"decimal\"\n [formControl]=\"control()\"\n [placeholder]=\"field().placeholder || ''\"\n [readonly]=\"readonly() || field().readonly\">\n @if (field().hint) {\n <ngs-hint>{{ field().hint }}</ngs-hint>\n }\n </ngs-form-field>\n }\n\n @case ('currency-select') {\n <ngs-form-field>\n <ngs-label>{{ field().label }}</ngs-label>\n <ngs-currency-select [formControl]=\"control()\"\n [placeholder]=\"field().placeholder || ''\"\n [required]=\"field().required ?? false\"/>\n @if (field().hint) {\n <ngs-hint>{{ field().hint }}</ngs-hint>\n }\n </ngs-form-field>\n }\n\n @case ('country-select') {\n <ngs-form-field>\n <ngs-label>{{ field().label }}</ngs-label>\n <ngs-country-select [formControl]=\"control()\"\n [placeholder]=\"field().placeholder || ''\"\n [clearable]=\"field().clearable ?? false\"\n [required]=\"field().required ?? false\"/>\n @if (field().hint) {\n <ngs-hint>{{ field().hint }}</ngs-hint>\n }\n </ngs-form-field>\n }\n\n @default {\n <ngs-form-field>\n <ngs-label>{{ field().label }}</ngs-label>\n <input ngsInput\n [type]=\"textInputType()\"\n [formControl]=\"control()\"\n [placeholder]=\"field().placeholder || ''\"\n [readonly]=\"readonly() || field().readonly\">\n @if (field().hint) {\n <ngs-hint>{{ field().hint }}</ngs-hint>\n }\n </ngs-form-field>\n }\n }\n}\n", styles: [":host{display:block;min-width:0}:host.is-width-1{grid-column:span 1}:host.is-width-2{grid-column:span 2}:host.is-width-3{grid-column:span 3}:host.is-width-4{grid-column:span 4}:host.is-width-5{grid-column:span 5}:host.is-width-6{grid-column:span 6}:host.is-width-7{grid-column:span 7}:host.is-width-8{grid-column:span 8}:host.is-width-9{grid-column:span 9}:host.is-width-10{grid-column:span 10}:host.is-width-11{grid-column:span 11}:host.is-width-12{grid-column:span 12}:host .ngs-form-builder-check-field{display:flex;min-height:calc(var(--spacing, .25rem) * 11);flex-direction:column;justify-content:center;gap:calc(var(--spacing, .25rem) * 1)}:host .ngs-form-builder-check-field span{color:var(--ngs-color-on-surface-variant);font-size:var(--ngs-font-size-xs)}:host .ngs-form-builder-choice-field{display:flex;min-height:calc(var(--spacing, .25rem) * 11);flex-direction:column;gap:calc(var(--spacing, .25rem) * 2)}:host .ngs-form-builder-upload-field{display:flex;flex-direction:column;gap:calc(var(--spacing, .25rem) * 2)}:host .ngs-form-builder-upload-field.is-disabled{pointer-events:none;opacity:.7}:host .ngs-form-builder-upload-field ngs-upload-area{--ngs-upload-area-padding: calc(var(--spacing, .25rem) * 6);--ngs-upload-area-border-radius: var(--ngs-field-radius)}:host .ngs-form-builder-choice-label{color:var(--ngs-color-on-surface);font-size:var(--ngs-font-size-sm);font-weight:500}:host .ngs-form-builder-choice-hint{color:var(--ngs-color-on-surface-variant);font-size:var(--ngs-font-size-xs)}:host .ngs-form-builder-spacer{min-height:calc(var(--spacing, .25rem) * 6)}:host .ngs-form-builder-spacer.is-height-8{min-height:calc(var(--spacing, .25rem) * 2)}:host .ngs-form-builder-spacer.is-height-16{min-height:calc(var(--spacing, .25rem) * 4)}:host .ngs-form-builder-spacer.is-height-24{min-height:calc(var(--spacing, .25rem) * 6)}:host .ngs-form-builder-spacer.is-height-32{min-height:calc(var(--spacing, .25rem) * 8)}:host .ngs-form-builder-spacer.is-height-48{min-height:calc(var(--spacing, .25rem) * 12)}:host .ngs-form-builder-spacer.is-height-64{min-height:calc(var(--spacing, .25rem) * 16)}\n/*! tailwindcss v4.2.2 | MIT License | https://tailwindcss.com */\n"], dependencies: [{ kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1.NumberValueAccessor, selector: "input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]" }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.RequiredValidator, selector: ":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]", inputs: ["required"] }, { kind: "directive", type: i1.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }, { kind: "component", type: Datepicker, selector: "ngs-datepicker", inputs: ["startAt", "calendarHeaderComponent", "quickPresets", "showQuickPresets"], exportAs: ["ngsDatepicker"] }, { kind: "directive", type: DatepickerInput, selector: "input[ngsDatepicker]", inputs: ["ngsDatepicker"], exportAs: ["ngsDatepickerInput"] }, { kind: "component", type: DatepickerToggle, selector: "ngs-datepicker-toggle", inputs: ["for"], exportAs: ["ngsDatepickerToggle"] }, { kind: "component", type: DateRangeInput, selector: "ngs-date-range-input", inputs: ["rangePicker", "separator"], exportAs: ["ngsDateRangeInput"] }, { kind: "component", type: DateRangePicker, selector: "ngs-date-range-picker", inputs: ["startAt", "calendarHeaderComponent", "quickPresets", "showQuickPresets", "calendarCount", "extended"], exportAs: ["ngsDateRangePicker"] }, { kind: "directive", type: StartDate, selector: "input[ngsStartDate]" }, { kind: "directive", type: EndDate, selector: "input[ngsEndDate]" }, { kind: "component", type: FormField, selector: "ngs-form-field", inputs: ["subscriptHiddenIfEmpty", "sameHeightAsButton"], exportAs: ["ngsFormField"] }, { kind: "component", type: Hint, selector: "ngs-hint", inputs: ["align"], exportAs: ["ngsHint"] }, { kind: "directive", type: IconButtonSuffix, selector: "[ngsIconButtonSuffix]" }, { kind: "component", type: Label, selector: "ngs-label" }, { kind: "directive", type: Input, selector: "input[ngsInput], textarea[ngsInput]", inputs: ["id", "placeholder", "required", "disabled", "readonly", "errorStateMatcher"], exportAs: ["ngsInput"] }, { kind: "component", type: Select, selector: "ngs-select", inputs: ["id", "placeholder", "disabled", "required", "multiple", "hideCheckIcon", "clearable", "aria-label", "tabIndex", "aria-describedby", "value"], outputs: ["selectionChange", "opened", "closed", "valueChange"], exportAs: ["ngsSelect"] }, { kind: "component", type: Option, selector: "ngs-option", inputs: ["value", "data", "disabled", "selected"], outputs: ["onSelectionChange"], exportAs: ["ngsOption"] }, { kind: "component", type: Checkbox, selector: "ngs-checkbox", inputs: ["aria-label", "aria-labelledby", "aria-describedby", "aria-expanded", "aria-controls", "aria-owns", "id", "required", "labelPosition", "name", "value", "disableRipple", "tabIndex", "color", "disabledInteractive", "checked", "disabled", "indeterminate"], outputs: ["checkedChange", "disabledChange", "indeterminateChange", "change"], exportAs: ["ngsCheckbox"] }, { kind: "component", type: SlideToggle, selector: "ngs-slide-toggle", inputs: ["id", "name", "labelPosition", "aria-label", "aria-labelledby", "aria-describedby", "required", "disabled", "disableRipple", "tabIndex", "hideIcon", "color", "checked"], outputs: ["disabledChange", "checkedChange", "change", "toggleChange"], exportAs: ["ngsSlideToggle"] }, { kind: "component", type: RadioButton, selector: "ngs-radio-button", inputs: ["id", "value", "name", "checked", "disabled"], outputs: ["checkedChange", "change"] }, { kind: "component", type: RadioGroup, selector: "ngs-radio-group", inputs: ["disabled", "name", "orientation", "value"], outputs: ["disabledChange", "valueChange", "change"], exportAs: ["ngsRadioGroup"] }, { kind: "component", type: CountrySelect, selector: "ngs-country-select", inputs: ["searchTerm", "value", "id", "placeholder", "required", "disabled", "multiple", "hideCheckIcon", "clearable", "aria-label", "tabIndex", "aria-describedby", "showCountryCode"], outputs: ["searchTermChange", "valueChange", "requiredChange", "disabledChange", "opened", "closed", "selectionChange"], exportAs: ["ngsCountrySelect"] }, { kind: "component", type: CurrencySelect, selector: "ngs-currency-select", inputs: ["searchTerm", "placeholder", "required", "disabled", "showCountryName"], outputs: ["searchTermChange", "requiredChange", "disabledChange", "opened", "closed"], exportAs: ["ngsCurrencySelect"] }, { kind: "component", type: TimezoneSelect, selector: "ngs-timezone-select", inputs: ["aria-describedby", "locale", "placeholder", "required", "disabled", "value", "searchTerm"], outputs: ["valueChange", "searchTermChange", "opened", "closed"], exportAs: ["ngsTimezoneSelect"] }, { kind: "component", type: Timepicker, selector: "ngs-timepicker", inputs: ["disabled", "interval"], outputs: ["opened", "closed"], exportAs: ["ngsTimepicker"] }, { kind: "directive", type: TimepickerInput, selector: "input[ngsTimepicker]", inputs: ["disabled", "max", "min", "openOnClick", "ngsTimepicker"] }, { kind: "component", type: TimepickerToggle, selector: "ngs-timepicker-toggle", inputs: ["for", "disabled"], exportAs: ["ngsTimepickerToggle"] }, { kind: "component", type: Icon, selector: "ngs-icon", inputs: ["name"], exportAs: ["ngsIcon"] }, { kind: "component", type: UploadArea, selector: "ngs-upload-area", inputs: ["accept", "allowHover", "multiple"], outputs: ["fileSelected"], exportAs: ["ngsUploadArea"] }, { kind: "directive", type: UploadAreaIconDirective, selector: "[ngsUploadAreaIcon]" }, { kind: "directive", type: UploadAreaMainStateDirective, selector: "[ngsUploadAreaMainState]" }, { kind: "directive", type: UploadAreaDropStateDirective, selector: "[ngsUploadAreaDropState]" }, { kind: "directive", type: UploadAreaInvalidStateDirective, selector: "[ngsUploadAreaInvalidState]" }, { kind: "directive", type: UploadTriggerDirective, selector: "[ngsUploadTrigger]", inputs: ["accept", "multiple"], outputs: ["fileSelected"], exportAs: ["ngsUploadTrigger"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
241
435
  }
242
436
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: FormBuilderFieldHost, decorators: [{
243
437
  type: Component,
@@ -246,6 +440,10 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.4", ngImpor
246
440
  Datepicker,
247
441
  DatepickerInput,
248
442
  DatepickerToggle,
443
+ DateRangeInput,
444
+ DateRangePicker,
445
+ StartDate,
446
+ EndDate,
249
447
  FormField,
250
448
  Hint,
251
449
  IconButtonSuffix,
@@ -254,7 +452,22 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.4", ngImpor
254
452
  Select,
255
453
  Option,
256
454
  Checkbox,
257
- SlideToggle
455
+ SlideToggle,
456
+ RadioButton,
457
+ RadioGroup,
458
+ CountrySelect,
459
+ CurrencySelect,
460
+ TimezoneSelect,
461
+ Timepicker,
462
+ TimepickerInput,
463
+ TimepickerToggle,
464
+ Icon,
465
+ UploadArea,
466
+ UploadAreaIconDirective,
467
+ UploadAreaMainStateDirective,
468
+ UploadAreaDropStateDirective,
469
+ UploadAreaInvalidStateDirective,
470
+ UploadTriggerDirective
258
471
  ], changeDetection: ChangeDetectionStrategy.OnPush, providers: [
259
472
  provideNativeDateAdapter()
260
473
  ], host: {
@@ -272,8 +485,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.4", ngImpor
272
485
  '[class.is-width-10]': '!editableCanvas() && field().width === 10',
273
486
  '[class.is-width-11]': '!editableCanvas() && field().width === 11',
274
487
  '[class.is-width-12]': '!editableCanvas() && (field().width ?? 12) === 12'
275
- }, template: "<ng-container #anchor/>\n\n@if (!customLoaded()) {\n @switch (field().type) {\n @case ('textarea') {\n <ngs-form-field>\n <ngs-label>{{ field().label }}</ngs-label>\n <textarea ngsInput\n rows=\"3\"\n [formControl]=\"control()\"\n [placeholder]=\"field().placeholder || ''\"\n [readonly]=\"readonly() || field().readonly\"></textarea>\n @if (field().hint) {\n <ngs-hint>{{ field().hint }}</ngs-hint>\n }\n </ngs-form-field>\n }\n\n @case ('select') {\n <ngs-form-field>\n <ngs-label>{{ field().label }}</ngs-label>\n <ngs-select [formControl]=\"control()\" [placeholder]=\"field().placeholder || ''\">\n @for (option of field().options ?? []; track option.value) {\n <ngs-option [value]=\"option.value\">{{ option.label }}</ngs-option>\n }\n </ngs-select>\n @if (field().hint) {\n <ngs-hint>{{ field().hint }}</ngs-hint>\n }\n </ngs-form-field>\n }\n\n @case ('checkbox') {\n <div class=\"ngs-form-builder-check-field\">\n <ngs-checkbox [formControl]=\"control()\">\n {{ field().label }}\n </ngs-checkbox>\n @if (field().hint) {\n <span>{{ field().hint }}</span>\n }\n </div>\n }\n\n @case ('toggle') {\n <div class=\"ngs-form-builder-check-field\">\n <ngs-slide-toggle [formControl]=\"control()\">\n {{ field().label }}\n </ngs-slide-toggle>\n @if (field().hint) {\n <span>{{ field().hint }}</span>\n }\n </div>\n }\n\n @case ('date') {\n <ngs-form-field>\n <ngs-label>{{ field().label }}</ngs-label>\n <input ngsInput\n [ngsDatepicker]=\"picker\"\n [formControl]=\"control()\"\n [placeholder]=\"field().placeholder || ''\"\n [readonly]=\"readonly() || field().readonly\">\n <ngs-datepicker-toggle ngsIconButtonSuffix [for]=\"picker\"/>\n <ngs-datepicker #picker/>\n @if (field().hint) {\n <ngs-hint>{{ field().hint }}</ngs-hint>\n }\n </ngs-form-field>\n }\n\n @case ('currency') {\n <ngs-form-field>\n <ngs-label>{{ field().label }}</ngs-label>\n <input ngsInput\n type=\"number\"\n inputmode=\"decimal\"\n [formControl]=\"control()\"\n [placeholder]=\"field().placeholder || ''\"\n [readonly]=\"readonly() || field().readonly\">\n @if (field().hint) {\n <ngs-hint>{{ field().hint }}</ngs-hint>\n }\n </ngs-form-field>\n }\n\n @default {\n <ngs-form-field>\n <ngs-label>{{ field().label }}</ngs-label>\n <input ngsInput\n [type]=\"textInputType()\"\n [formControl]=\"control()\"\n [placeholder]=\"field().placeholder || ''\"\n [readonly]=\"readonly() || field().readonly\">\n @if (field().hint) {\n <ngs-hint>{{ field().hint }}</ngs-hint>\n }\n </ngs-form-field>\n }\n }\n}\n", styles: [":host{display:block;min-width:0}:host.is-width-1{grid-column:span 1}:host.is-width-2{grid-column:span 2}:host.is-width-3{grid-column:span 3}:host.is-width-4{grid-column:span 4}:host.is-width-5{grid-column:span 5}:host.is-width-6{grid-column:span 6}:host.is-width-7{grid-column:span 7}:host.is-width-8{grid-column:span 8}:host.is-width-9{grid-column:span 9}:host.is-width-10{grid-column:span 10}:host.is-width-11{grid-column:span 11}:host.is-width-12{grid-column:span 12}:host .ngs-form-builder-check-field{display:flex;min-height:calc(var(--spacing, .25rem) * 11);flex-direction:column;justify-content:center;gap:calc(var(--spacing, .25rem) * 1)}:host .ngs-form-builder-check-field span{color:var(--ngs-color-on-surface-variant);font-size:var(--ngs-font-size-xs)}\n/*! tailwindcss v4.2.2 | MIT License | https://tailwindcss.com */\n"] }]
276
- }], ctorParameters: () => [], propDecorators: { field: [{ type: i0.Input, args: [{ isSignal: true, alias: "field", required: true }] }], control: [{ type: i0.Input, args: [{ isSignal: true, alias: "control", required: true }] }], definitions: [{ type: i0.Input, args: [{ isSignal: true, alias: "definitions", required: false }] }], readonly: [{ type: i0.Input, args: [{ isSignal: true, alias: "readonly", required: false }] }], editableCanvas: [{ type: i0.Input, args: [{ isSignal: true, alias: "editableCanvas", required: false }] }], anchor: [{ type: i0.ViewChild, args: ['anchor', { ...{ read: ViewContainerRef }, isSignal: true }] }] } });
488
+ }, template: "<ng-container #anchor/>\n\n@if (!customLoaded()) {\n @switch (field().type) {\n @case ('textarea') {\n <ngs-form-field>\n <ngs-label>{{ field().label }}</ngs-label>\n <textarea ngsInput\n rows=\"3\"\n [formControl]=\"control()\"\n [placeholder]=\"field().placeholder || ''\"\n [readonly]=\"readonly() || field().readonly\"></textarea>\n @if (field().hint) {\n <ngs-hint>{{ field().hint }}</ngs-hint>\n }\n </ngs-form-field>\n }\n\n @case ('select') {\n <ngs-form-field>\n <ngs-label>{{ field().label }}</ngs-label>\n <ngs-select [formControl]=\"control()\"\n [placeholder]=\"field().placeholder || ''\"\n [multiple]=\"field().multiple ?? false\"\n [clearable]=\"field().clearable ?? false\">\n @for (option of field().options ?? []; track option.value) {\n <ngs-option [value]=\"option.value\">{{ option.label }}</ngs-option>\n }\n </ngs-select>\n @if (field().hint) {\n <ngs-hint>{{ field().hint }}</ngs-hint>\n }\n </ngs-form-field>\n }\n\n @case ('radio') {\n <div class=\"ngs-form-builder-choice-field\">\n <span class=\"ngs-form-builder-choice-label\">{{ field().label }}</span>\n <ngs-radio-group [formControl]=\"control()\"\n [name]=\"field().name\"\n [orientation]=\"radioOrientation()\">\n @for (option of field().options ?? []; track option.value) {\n <ngs-radio-button [value]=\"option.value\">{{ option.label }}</ngs-radio-button>\n }\n </ngs-radio-group>\n @if (field().hint) {\n <span class=\"ngs-form-builder-choice-hint\">{{ field().hint }}</span>\n }\n </div>\n }\n\n @case ('checkbox') {\n <div class=\"ngs-form-builder-check-field\">\n <ngs-checkbox [formControl]=\"control()\">\n {{ field().label }}\n </ngs-checkbox>\n @if (field().hint) {\n <span>{{ field().hint }}</span>\n }\n </div>\n }\n\n @case ('toggle') {\n <div class=\"ngs-form-builder-check-field\">\n <ngs-slide-toggle [formControl]=\"control()\">\n {{ field().label }}\n </ngs-slide-toggle>\n @if (field().hint) {\n <span>{{ field().hint }}</span>\n }\n </div>\n }\n\n @case ('date') {\n <ngs-form-field>\n <ngs-label>{{ field().label }}</ngs-label>\n <input ngsInput\n [ngsDatepicker]=\"picker\"\n [formControl]=\"control()\"\n [placeholder]=\"field().placeholder || ''\"\n [readonly]=\"readonly() || field().readonly\">\n <ngs-datepicker-toggle ngsIconButtonSuffix [for]=\"picker\"/>\n <ngs-datepicker #picker/>\n @if (field().hint) {\n <ngs-hint>{{ field().hint }}</ngs-hint>\n }\n </ngs-form-field>\n }\n\n @case ('time') {\n <ngs-form-field>\n <ngs-label>{{ field().label }}</ngs-label>\n <input ngsInput\n [ngsTimepicker]=\"timePicker\"\n [formControl]=\"control()\"\n [placeholder]=\"field().placeholder || ''\"\n [readonly]=\"readonly() || field().readonly\">\n <ngs-timepicker-toggle ngsIconButtonSuffix [for]=\"timePicker\"/>\n <ngs-timepicker #timePicker/>\n @if (field().hint) {\n <ngs-hint>{{ field().hint }}</ngs-hint>\n }\n </ngs-form-field>\n }\n\n @case ('date-range') {\n <ngs-form-field>\n <ngs-label>{{ field().label }}</ngs-label>\n <ngs-date-range-input #rangeInput [rangePicker]=\"rangePicker\">\n <input ngsStartDate\n [value]=\"dateRangeStartValue()\"\n [placeholder]=\"field().placeholder || 'Start date'\"\n [readonly]=\"readonly() || field().readonly\"\n [disabled]=\"dateRangeDisabled()\"\n (input)=\"onDateRangeChanged(rangeInput)\">\n <input ngsEndDate\n [value]=\"dateRangeEndValue()\"\n placeholder=\"End date\"\n [readonly]=\"readonly() || field().readonly\"\n [disabled]=\"dateRangeDisabled()\"\n (input)=\"onDateRangeChanged(rangeInput)\">\n </ngs-date-range-input>\n <ngs-datepicker-toggle ngsIconButtonSuffix [for]=\"rangePicker\"/>\n <ngs-date-range-picker #rangePicker/>\n @if (field().hint) {\n <ngs-hint>{{ field().hint }}</ngs-hint>\n }\n </ngs-form-field>\n }\n\n @case ('upload') {\n <div class=\"ngs-form-builder-upload-field\"\n [class.is-disabled]=\"uploadDisabled()\">\n <span class=\"ngs-form-builder-choice-label\">{{ field().label }}</span>\n <ngs-upload-area ngsUploadTrigger\n [accept]=\"uploadAccept()\"\n [multiple]=\"field().multiple ?? false\"\n (fileSelected)=\"onUploadFilesSelected($event)\">\n <ngs-icon name=\"fluent:arrow-upload-24-regular\" ngsUploadAreaIcon/>\n <ng-container ngsUploadAreaMainState>\n {{ uploadSelectedText() }}\n </ng-container>\n <ng-container ngsUploadAreaDropState>\n Drop files here\n </ng-container>\n <ng-container ngsUploadAreaInvalidState>\n Invalid file type\n </ng-container>\n </ngs-upload-area>\n @if (field().hint) {\n <span class=\"ngs-form-builder-choice-hint\">{{ field().hint }}</span>\n }\n </div>\n }\n\n @case ('spacer') {\n <div class=\"ngs-form-builder-spacer\"\n [class.is-height-8]=\"spacerHeight() === 8\"\n [class.is-height-16]=\"spacerHeight() === 16\"\n [class.is-height-24]=\"spacerHeight() === 24\"\n [class.is-height-32]=\"spacerHeight() === 32\"\n [class.is-height-48]=\"spacerHeight() === 48\"\n [class.is-height-64]=\"spacerHeight() === 64\"\n aria-hidden=\"true\"></div>\n }\n\n @case ('timezone-select') {\n <ngs-form-field>\n <ngs-label>{{ field().label }}</ngs-label>\n <ngs-timezone-select [formControl]=\"control()\"\n [placeholder]=\"field().placeholder || ''\"\n [required]=\"field().required ?? false\"/>\n @if (field().hint) {\n <ngs-hint>{{ field().hint }}</ngs-hint>\n }\n </ngs-form-field>\n }\n\n @case ('currency') {\n <ngs-form-field>\n <ngs-label>{{ field().label }}</ngs-label>\n <input ngsInput\n type=\"number\"\n inputmode=\"decimal\"\n [formControl]=\"control()\"\n [placeholder]=\"field().placeholder || ''\"\n [readonly]=\"readonly() || field().readonly\">\n @if (field().hint) {\n <ngs-hint>{{ field().hint }}</ngs-hint>\n }\n </ngs-form-field>\n }\n\n @case ('currency-select') {\n <ngs-form-field>\n <ngs-label>{{ field().label }}</ngs-label>\n <ngs-currency-select [formControl]=\"control()\"\n [placeholder]=\"field().placeholder || ''\"\n [required]=\"field().required ?? false\"/>\n @if (field().hint) {\n <ngs-hint>{{ field().hint }}</ngs-hint>\n }\n </ngs-form-field>\n }\n\n @case ('country-select') {\n <ngs-form-field>\n <ngs-label>{{ field().label }}</ngs-label>\n <ngs-country-select [formControl]=\"control()\"\n [placeholder]=\"field().placeholder || ''\"\n [clearable]=\"field().clearable ?? false\"\n [required]=\"field().required ?? false\"/>\n @if (field().hint) {\n <ngs-hint>{{ field().hint }}</ngs-hint>\n }\n </ngs-form-field>\n }\n\n @default {\n <ngs-form-field>\n <ngs-label>{{ field().label }}</ngs-label>\n <input ngsInput\n [type]=\"textInputType()\"\n [formControl]=\"control()\"\n [placeholder]=\"field().placeholder || ''\"\n [readonly]=\"readonly() || field().readonly\">\n @if (field().hint) {\n <ngs-hint>{{ field().hint }}</ngs-hint>\n }\n </ngs-form-field>\n }\n }\n}\n", styles: [":host{display:block;min-width:0}:host.is-width-1{grid-column:span 1}:host.is-width-2{grid-column:span 2}:host.is-width-3{grid-column:span 3}:host.is-width-4{grid-column:span 4}:host.is-width-5{grid-column:span 5}:host.is-width-6{grid-column:span 6}:host.is-width-7{grid-column:span 7}:host.is-width-8{grid-column:span 8}:host.is-width-9{grid-column:span 9}:host.is-width-10{grid-column:span 10}:host.is-width-11{grid-column:span 11}:host.is-width-12{grid-column:span 12}:host .ngs-form-builder-check-field{display:flex;min-height:calc(var(--spacing, .25rem) * 11);flex-direction:column;justify-content:center;gap:calc(var(--spacing, .25rem) * 1)}:host .ngs-form-builder-check-field span{color:var(--ngs-color-on-surface-variant);font-size:var(--ngs-font-size-xs)}:host .ngs-form-builder-choice-field{display:flex;min-height:calc(var(--spacing, .25rem) * 11);flex-direction:column;gap:calc(var(--spacing, .25rem) * 2)}:host .ngs-form-builder-upload-field{display:flex;flex-direction:column;gap:calc(var(--spacing, .25rem) * 2)}:host .ngs-form-builder-upload-field.is-disabled{pointer-events:none;opacity:.7}:host .ngs-form-builder-upload-field ngs-upload-area{--ngs-upload-area-padding: calc(var(--spacing, .25rem) * 6);--ngs-upload-area-border-radius: var(--ngs-field-radius)}:host .ngs-form-builder-choice-label{color:var(--ngs-color-on-surface);font-size:var(--ngs-font-size-sm);font-weight:500}:host .ngs-form-builder-choice-hint{color:var(--ngs-color-on-surface-variant);font-size:var(--ngs-font-size-xs)}:host .ngs-form-builder-spacer{min-height:calc(var(--spacing, .25rem) * 6)}:host .ngs-form-builder-spacer.is-height-8{min-height:calc(var(--spacing, .25rem) * 2)}:host .ngs-form-builder-spacer.is-height-16{min-height:calc(var(--spacing, .25rem) * 4)}:host .ngs-form-builder-spacer.is-height-24{min-height:calc(var(--spacing, .25rem) * 6)}:host .ngs-form-builder-spacer.is-height-32{min-height:calc(var(--spacing, .25rem) * 8)}:host .ngs-form-builder-spacer.is-height-48{min-height:calc(var(--spacing, .25rem) * 12)}:host .ngs-form-builder-spacer.is-height-64{min-height:calc(var(--spacing, .25rem) * 16)}\n/*! tailwindcss v4.2.2 | MIT License | https://tailwindcss.com */\n"] }]
489
+ }], ctorParameters: () => [], propDecorators: { field: [{ type: i0.Input, args: [{ isSignal: true, alias: "field", required: true }] }], control: [{ type: i0.Input, args: [{ isSignal: true, alias: "control", required: true }] }], definitions: [{ type: i0.Input, args: [{ isSignal: true, alias: "definitions", required: false }] }], readonly: [{ type: i0.Input, args: [{ isSignal: true, alias: "readonly", required: false }] }], editableCanvas: [{ type: i0.Input, args: [{ isSignal: true, alias: "editableCanvas", required: false }] }], uploadCallback: [{ type: i0.Input, args: [{ isSignal: true, alias: "uploadCallback", required: false }] }], anchor: [{ type: i0.ViewChild, args: ['anchor', { ...{ read: ViewContainerRef }, isSignal: true }] }] } });
277
490
 
278
491
  class FormBuilderRenderer {
279
492
  providedItems = inject(FORM_BUILDER_ITEMS, { optional: true }) ?? [];
@@ -283,6 +496,7 @@ class FormBuilderRenderer {
283
496
  readonly = input(false, ...(ngDevMode ? [{ debugName: "readonly" }] : /* istanbul ignore next */ []));
284
497
  showSubmit = input(true, ...(ngDevMode ? [{ debugName: "showSubmit" }] : /* istanbul ignore next */ []));
285
498
  submitLabel = input('Submit', ...(ngDevMode ? [{ debugName: "submitLabel" }] : /* istanbul ignore next */ []));
499
+ uploadCallback = input(undefined, ...(ngDevMode ? [{ debugName: "uploadCallback" }] : /* istanbul ignore next */ []));
286
500
  value = model({}, ...(ngDevMode ? [{ debugName: "value" }] : /* istanbul ignore next */ []));
287
501
  formSubmit = output();
288
502
  formReady = output();
@@ -346,7 +560,7 @@ class FormBuilderRenderer {
346
560
  return existing;
347
561
  }
348
562
  const nextControl = new FormControl({
349
- value: field.defaultValue ?? null,
563
+ value: this.fieldInitialValue(field),
350
564
  disabled: true
351
565
  });
352
566
  this.orphanControls.set(field.id, nextControl);
@@ -385,7 +599,7 @@ class FormBuilderRenderer {
385
599
  }
386
600
  const validators = definition?.validators?.(field) ?? validatorsFromRules(field.validation, field);
387
601
  const control = new FormControl({
388
- value: value[field.name] ?? field.defaultValue ?? null,
602
+ value: value[field.name] ?? this.fieldInitialValue(field),
389
603
  disabled: field.disabled || this.readonly()
390
604
  }, validators);
391
605
  controls[field.name] = control;
@@ -400,6 +614,21 @@ class FormBuilderRenderer {
400
614
  children: field.children ? this.visibleFields(field.children) : undefined
401
615
  }));
402
616
  }
617
+ fieldInitialValue(field) {
618
+ if (field.defaultValue !== undefined) {
619
+ return field.defaultValue;
620
+ }
621
+ if (field.type === 'upload') {
622
+ return field.multiple ? [] : null;
623
+ }
624
+ const selectedValues = (field.options ?? [])
625
+ .filter(option => option.selected)
626
+ .map(option => option.value);
627
+ if (field.type === 'checkbox-list' || field.multiple) {
628
+ return selectedValues;
629
+ }
630
+ return selectedValues[0] ?? null;
631
+ }
403
632
  resolveCanvasItems(schema) {
404
633
  const fieldsById = new Map((schema.fields ?? []).map(field => [field.id, field]));
405
634
  const sectionsById = new Map(schema.sections.map(section => [section.id, section]));
@@ -420,19 +649,19 @@ class FormBuilderRenderer {
420
649
  return items;
421
650
  }
422
651
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: FormBuilderRenderer, deps: [], target: i0.ɵɵFactoryTarget.Component });
423
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.4", type: FormBuilderRenderer, isStandalone: true, selector: "ngs-form-builder-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 }, value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { value: "valueChange", formSubmit: "formSubmit", formReady: "formReady" }, host: { classAttribute: "ngs-form-builder-renderer" }, exportAs: ["ngsFormBuilderRenderer"], ngImport: i0, template: "<ng-template #renderFields let-fields>\n <div class=\"ngs-form-builder-renderer-grid\">\n @for (field of fields; track field.id) {\n @if (isContainerField(field)) {\n <section class=\"ngs-form-builder-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 [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-builder-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-builder-renderer-section{display:flex;flex-direction:column;gap:calc(var(--spacing, .25rem) * 4)}:host .ngs-form-builder-renderer-section header{display:flex;flex-direction:column;gap:calc(var(--spacing, .25rem) * 1)}:host .ngs-form-builder-renderer-section header h3{color:var(--ngs-color-on-surface);font-size:var(--ngs-font-size-lg);font-weight:600}:host .ngs-form-builder-renderer-section header p{color:var(--ngs-color-on-surface-variant);font-size:var(--ngs-font-size-sm)}:host .ngs-form-builder-renderer-grid{display:grid;grid-template-columns:repeat(12,minmax(0,1fr));gap:calc(var(--spacing, .25rem) * 4)}:host .ngs-form-builder-renderer-grid-field{display:flex;grid-column:1/-1;flex-direction:column;gap:calc(var(--spacing, .25rem) * 4);border:1px solid var(--ngs-color-outline-variant);border-radius:var(--ngs-radius-lg);padding:calc(var(--spacing, .25rem) * 4)}:host .ngs-form-builder-renderer-grid-field>header h4{color:var(--ngs-color-on-surface);font-size:var(--ngs-font-size-base);font-weight:600}:host .ngs-form-builder-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"], exportAs: ["ngsFormBuilderFieldHost"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
652
+ 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 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 });
424
653
  }
425
654
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: FormBuilderRenderer, decorators: [{
426
655
  type: Component,
427
- args: [{ selector: 'ngs-form-builder-renderer', exportAs: 'ngsFormBuilderRenderer', imports: [
656
+ args: [{ selector: 'ngs-form-renderer', exportAs: 'ngsFormRenderer', imports: [
428
657
  NgTemplateOutlet,
429
658
  ReactiveFormsModule,
430
659
  Button,
431
660
  FormBuilderFieldHost
432
661
  ], changeDetection: ChangeDetectionStrategy.OnPush, host: {
433
- 'class': 'ngs-form-builder-renderer'
434
- }, template: "<ng-template #renderFields let-fields>\n <div class=\"ngs-form-builder-renderer-grid\">\n @for (field of fields; track field.id) {\n @if (isContainerField(field)) {\n <section class=\"ngs-form-builder-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 [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-builder-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-builder-renderer-section{display:flex;flex-direction:column;gap:calc(var(--spacing, .25rem) * 4)}:host .ngs-form-builder-renderer-section header{display:flex;flex-direction:column;gap:calc(var(--spacing, .25rem) * 1)}:host .ngs-form-builder-renderer-section header h3{color:var(--ngs-color-on-surface);font-size:var(--ngs-font-size-lg);font-weight:600}:host .ngs-form-builder-renderer-section header p{color:var(--ngs-color-on-surface-variant);font-size:var(--ngs-font-size-sm)}:host .ngs-form-builder-renderer-grid{display:grid;grid-template-columns:repeat(12,minmax(0,1fr));gap:calc(var(--spacing, .25rem) * 4)}:host .ngs-form-builder-renderer-grid-field{display:flex;grid-column:1/-1;flex-direction:column;gap:calc(var(--spacing, .25rem) * 4);border:1px solid var(--ngs-color-outline-variant);border-radius:var(--ngs-radius-lg);padding:calc(var(--spacing, .25rem) * 4)}:host .ngs-form-builder-renderer-grid-field>header h4{color:var(--ngs-color-on-surface);font-size:var(--ngs-font-size-base);font-weight:600}:host .ngs-form-builder-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"] }]
435
- }], 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 }] }], 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"] }] } });
662
+ 'class': 'ngs-form-renderer'
663
+ }, 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 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"] }]
664
+ }], 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"] }] } });
436
665
  function normalizeFieldDefinition$1(definition) {
437
666
  return {
438
667
  ...definition,
@@ -475,50 +704,199 @@ function normalizedLayout(schema) {
475
704
  }
476
705
 
477
706
  class BasicFormBuilderFieldSettings {
707
+ destroyRef = inject(DestroyRef);
478
708
  field = input.required(...(ngDevMode ? [{ debugName: "field" }] : /* istanbul ignore next */ []));
479
709
  update = input.required(...(ngDevMode ? [{ debugName: "update" }] : /* istanbul ignore next */ []));
710
+ optionsControl = new FormControl('', {
711
+ nonNullable: true,
712
+ validators: optionsTextValidator
713
+ });
480
714
  fieldWidthOptions = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12];
715
+ radioOrientationOptions = ['vertical', 'horizontal'];
716
+ spacerHeightOptions = [8, 16, 24, 32, 48, 64];
481
717
  hasCheckedState = computed(() => ['checkbox', 'toggle'].includes(this.field().type), ...(ngDevMode ? [{ debugName: "hasCheckedState" }] : /* istanbul ignore next */ []));
482
- hasPlaceholder = computed(() => !['checkbox', 'toggle'].includes(this.field().type), ...(ngDevMode ? [{ debugName: "hasPlaceholder" }] : /* istanbul ignore next */ []));
718
+ hasPlaceholder = computed(() => !['checkbox', 'toggle', 'radio', 'spacer'].includes(this.field().type), ...(ngDevMode ? [{ debugName: "hasPlaceholder" }] : /* istanbul ignore next */ []));
483
719
  hasOptions = computed(() => ['select', 'radio', 'checkbox-list'].includes(this.field().type), ...(ngDevMode ? [{ debugName: "hasOptions" }] : /* istanbul ignore next */ []));
720
+ hasClearable = computed(() => ['select', 'country-select'].includes(this.field().type), ...(ngDevMode ? [{ debugName: "hasClearable" }] : /* istanbul ignore next */ []));
721
+ hasMultiple = computed(() => ['select', 'upload'].includes(this.field().type), ...(ngDevMode ? [{ debugName: "hasMultiple" }] : /* istanbul ignore next */ []));
722
+ isSelect = computed(() => this.field().type === 'select', ...(ngDevMode ? [{ debugName: "isSelect" }] : /* istanbul ignore next */ []));
723
+ isUpload = computed(() => this.field().type === 'upload', ...(ngDevMode ? [{ debugName: "isUpload" }] : /* istanbul ignore next */ []));
724
+ isRadio = computed(() => this.field().type === 'radio', ...(ngDevMode ? [{ debugName: "isRadio" }] : /* istanbul ignore next */ []));
725
+ isSpacer = computed(() => this.field().type === 'spacer', ...(ngDevMode ? [{ debugName: "isSpacer" }] : /* istanbul ignore next */ []));
726
+ hasBehaviorToggles = computed(() => !this.isSpacer(), ...(ngDevMode ? [{ debugName: "hasBehaviorToggles" }] : /* istanbul ignore next */ []));
727
+ radioOrientation = computed(() => this.field().settings?.['orientation'] === 'horizontal' ? 'horizontal' : 'vertical', ...(ngDevMode ? [{ debugName: "radioOrientation" }] : /* istanbul ignore next */ []));
484
728
  optionsText = computed(() => (this.field().options ?? [])
485
- .map(option => `${option.label}:${option.value}`)
729
+ .map(option => `${option.label}:${option.value}${this.isOptionSelected(option) ? ':selected' : ''}`)
486
730
  .join('\n'), ...(ngDevMode ? [{ debugName: "optionsText" }] : /* istanbul ignore next */ []));
731
+ constructor() {
732
+ effect(() => {
733
+ const nextValue = this.optionsText();
734
+ if (this.optionsControl.value !== nextValue) {
735
+ this.optionsControl.setValue(nextValue, { emitEvent: false });
736
+ }
737
+ });
738
+ this.optionsControl.valueChanges
739
+ .pipe(takeUntilDestroyed(this.destroyRef))
740
+ .subscribe(value => {
741
+ if (this.optionsControl.invalid) {
742
+ return;
743
+ }
744
+ this.patchOptions(value);
745
+ });
746
+ }
487
747
  patch(changes) {
488
748
  this.update()(changes);
489
749
  }
750
+ patchSettings(changes) {
751
+ this.patch({
752
+ settings: {
753
+ ...this.field().settings,
754
+ ...changes
755
+ }
756
+ });
757
+ }
758
+ patchMultiple(multiple) {
759
+ if (this.field().type === 'select') {
760
+ this.patchSelectMultiple(multiple);
761
+ return;
762
+ }
763
+ this.patch({
764
+ multiple,
765
+ defaultValue: multiple ? [] : null
766
+ });
767
+ }
768
+ patchSelectMultiple(multiple) {
769
+ const field = this.field();
770
+ const options = multiple ? field.options ?? [] : this.normalizeSelectedOptions(field.options ?? [], false);
771
+ const selectedValues = this.selectedOptionValues(options);
772
+ let defaultValue = null;
773
+ if (multiple) {
774
+ defaultValue = Array.isArray(field.defaultValue)
775
+ ? field.defaultValue
776
+ : field.defaultValue == null
777
+ ? selectedValues
778
+ : [field.defaultValue];
779
+ }
780
+ else {
781
+ defaultValue = Array.isArray(field.defaultValue)
782
+ ? field.defaultValue[0] ?? selectedValues[0] ?? null
783
+ : field.defaultValue ?? selectedValues[0] ?? null;
784
+ }
785
+ this.patch({ multiple, options, defaultValue });
786
+ }
787
+ patchRadioOrientation(orientation) {
788
+ this.patch({
789
+ settings: {
790
+ ...this.field().settings,
791
+ orientation
792
+ }
793
+ });
794
+ }
490
795
  patchOptions(value) {
491
- const options = value
796
+ const options = this.normalizeSelectedOptions(value
492
797
  .split('\n')
493
798
  .map(line => line.trim())
494
799
  .filter(Boolean)
495
800
  .map((line, index) => {
496
- const [label, optionValue] = line.split(':');
801
+ const parsed = parseOptionLine(line);
497
802
  return {
498
- label: label?.trim() || `Option ${index + 1}`,
499
- value: (optionValue ?? label ?? `option_${index + 1}`).trim()
803
+ label: parsed?.label || `Option ${index + 1}`,
804
+ value: parsed?.value || `option_${index + 1}`,
805
+ selected: parsed?.selected || undefined
500
806
  };
807
+ }));
808
+ this.patch({
809
+ options,
810
+ defaultValue: this.resolveSelectedDefaultValue(options)
501
811
  });
502
- this.patch({ options });
812
+ }
813
+ normalizeSelectedOptions(options, multiple = this.field().multiple) {
814
+ if (this.field().type === 'checkbox-list' || multiple) {
815
+ return options;
816
+ }
817
+ let selectedSeen = false;
818
+ return options.map(option => {
819
+ if (!option.selected) {
820
+ return option;
821
+ }
822
+ if (selectedSeen) {
823
+ return {
824
+ ...option,
825
+ selected: undefined
826
+ };
827
+ }
828
+ selectedSeen = true;
829
+ return option;
830
+ });
831
+ }
832
+ resolveSelectedDefaultValue(options) {
833
+ const selectedValues = this.selectedOptionValues(options);
834
+ if (this.field().type === 'checkbox-list' || this.field().multiple) {
835
+ return selectedValues;
836
+ }
837
+ return selectedValues[0] ?? null;
838
+ }
839
+ selectedOptionValues(options) {
840
+ return options
841
+ .filter(option => option.selected)
842
+ .map(option => option.value);
843
+ }
844
+ isOptionSelected(option) {
845
+ if (option.selected) {
846
+ return true;
847
+ }
848
+ const defaultValue = this.field().defaultValue;
849
+ return Array.isArray(defaultValue)
850
+ ? defaultValue.includes(option.value)
851
+ : defaultValue === option.value;
503
852
  }
504
853
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: BasicFormBuilderFieldSettings, deps: [], target: i0.ɵɵFactoryTarget.Component });
505
- 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 <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 @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 <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 <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 <div class=\"flex flex-col gap-5 py-1\">\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 </div>\n\n @if (hasOptions()) {\n <ngs-form-field>\n <ngs-label>Options</ngs-label>\n <textarea ngsInput\n rows=\"4\"\n [ngModel]=\"optionsText()\"\n [ngModelOptions]=\"{standalone: true}\"\n (ngModelChange)=\"patchOptions($event)\"></textarea>\n <ngs-hint>One option per line. Use Label:value to set explicit values.</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: "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"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
854
+ 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 });
506
855
  }
507
856
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: BasicFormBuilderFieldSettings, decorators: [{
508
857
  type: Component,
509
858
  args: [{ selector: 'ngs-basic-form-builder-field-settings', exportAs: 'ngsBasicFormBuilderFieldSettings', imports: [
510
859
  FormsModule,
860
+ ReactiveFormsModule,
861
+ Error,
511
862
  FormField,
512
863
  Hint,
513
864
  Label,
514
865
  Input,
515
866
  Select,
516
867
  Option,
517
- SlideToggle
868
+ SlideToggle,
869
+ SlideToggleGroup
518
870
  ], changeDetection: ChangeDetectionStrategy.OnPush, host: {
519
871
  'class': 'ngs-basic-form-builder-field-settings'
520
- }, 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 <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 @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 <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 <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 <div class=\"flex flex-col gap-5 py-1\">\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 </div>\n\n @if (hasOptions()) {\n <ngs-form-field>\n <ngs-label>Options</ngs-label>\n <textarea ngsInput\n rows=\"4\"\n [ngModel]=\"optionsText()\"\n [ngModelOptions]=\"{standalone: true}\"\n (ngModelChange)=\"patchOptions($event)\"></textarea>\n <ngs-hint>One option per line. Use Label:value to set explicit values.</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"] }]
521
- }], propDecorators: { field: [{ type: i0.Input, args: [{ isSignal: true, alias: "field", required: true }] }], update: [{ type: i0.Input, args: [{ isSignal: true, alias: "update", required: true }] }] } });
872
+ }, 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"] }]
873
+ }], ctorParameters: () => [], propDecorators: { field: [{ type: i0.Input, args: [{ isSignal: true, alias: "field", required: true }] }], update: [{ type: i0.Input, args: [{ isSignal: true, alias: "update", required: true }] }] } });
874
+ function optionsTextValidator(control) {
875
+ const hasInvalidLine = (control.value ?? '')
876
+ .split('\n')
877
+ .map(line => line.trim())
878
+ .filter(Boolean)
879
+ .some(line => !parseOptionLine(line));
880
+ return hasInvalidLine ? { formBuilderOptionsFormat: true } : null;
881
+ }
882
+ function parseOptionLine(line) {
883
+ const parts = line.split(':').map(part => part.trim());
884
+ if (parts.length < 2 || parts.length > 3) {
885
+ return null;
886
+ }
887
+ const [label, value, selected] = parts;
888
+ if (!label || !value) {
889
+ return null;
890
+ }
891
+ if (selected !== undefined && selected !== 'selected') {
892
+ return null;
893
+ }
894
+ return {
895
+ label,
896
+ value,
897
+ selected: selected === 'selected'
898
+ };
899
+ }
522
900
 
523
901
  class BasicFormBuilderLayoutSettings {
524
902
  field = input.required(...(ngDevMode ? [{ debugName: "field" }] : /* istanbul ignore next */ []));
@@ -659,6 +1037,7 @@ class FormBuilder {
659
1037
  schema = model(createDefaultFormBuilderSchema(), ...(ngDevMode ? [{ debugName: "schema" }] : /* istanbul ignore next */ []));
660
1038
  paletteTitle = input('Fields', ...(ngDevMode ? [{ debugName: "paletteTitle" }] : /* istanbul ignore next */ []));
661
1039
  inspectorTitle = input('Field properties', ...(ngDevMode ? [{ debugName: "inspectorTitle" }] : /* istanbul ignore next */ []));
1040
+ uploadCallback = input(undefined, ...(ngDevMode ? [{ debugName: "uploadCallback" }] : /* istanbul ignore next */ []));
662
1041
  fieldSelected = output();
663
1042
  fieldAdded = output();
664
1043
  fieldRemoved = output();
@@ -1079,7 +1458,7 @@ class FormBuilder {
1079
1458
  return existing;
1080
1459
  }
1081
1460
  const control = new FormControl({
1082
- value: field.defaultValue ?? null,
1461
+ value: this.fieldInitialValue(field),
1083
1462
  disabled: true
1084
1463
  });
1085
1464
  this.previewControls.set(field.id, control);
@@ -1298,7 +1677,7 @@ class FormBuilder {
1298
1677
  const baseLabel = definition.defaults?.label || definition.label;
1299
1678
  const name = uniqueFieldName(toFieldName(baseLabel), schema);
1300
1679
  const kind = definition.defaults?.kind ?? definition.kind ?? 'field';
1301
- return {
1680
+ const field = {
1302
1681
  id: uniqueId('field'),
1303
1682
  name,
1304
1683
  type: definition.type,
@@ -1312,6 +1691,10 @@ class FormBuilder {
1312
1691
  },
1313
1692
  ...definition.defaults
1314
1693
  };
1694
+ if (field.defaultValue === undefined) {
1695
+ field.defaultValue = this.fieldInitialValue(field);
1696
+ }
1697
+ return field;
1315
1698
  }
1316
1699
  defaultWidth(type) {
1317
1700
  return 12;
@@ -1608,7 +1991,7 @@ class FormBuilder {
1608
1991
  return;
1609
1992
  }
1610
1993
  queueMicrotask(() => {
1611
- const value = field.defaultValue ?? null;
1994
+ const value = this.fieldInitialValue(field);
1612
1995
  if (control.value !== value) {
1613
1996
  control.setValue(value, { emitEvent: false });
1614
1997
  }
@@ -1617,6 +2000,21 @@ class FormBuilder {
1617
2000
  }
1618
2001
  });
1619
2002
  }
2003
+ fieldInitialValue(field) {
2004
+ if (field.defaultValue !== undefined) {
2005
+ return field.defaultValue;
2006
+ }
2007
+ if (field.type === 'upload') {
2008
+ return field.multiple ? [] : null;
2009
+ }
2010
+ const selectedValues = (field.options ?? [])
2011
+ .filter(option => option.selected)
2012
+ .map(option => option.value);
2013
+ if (field.type === 'checkbox-list' || field.multiple) {
2014
+ return selectedValues;
2015
+ }
2016
+ return selectedValues[0] ?? null;
2017
+ }
1620
2018
  scrollElementFullyIntoView(target, scrollableContent) {
1621
2019
  const padding = 16;
1622
2020
  const containerRect = scrollableContent.getBoundingClientRect();
@@ -1768,7 +2166,7 @@ class FormBuilder {
1768
2166
  }
1769
2167
  }
1770
2168
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: FormBuilder, deps: [], target: i0.ɵɵFactoryTarget.Component });
1771
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.4", type: FormBuilder, isStandalone: true, selector: "ngs-form-builder", inputs: { schema: { classPropertyName: "schema", publicName: "schema", isSignal: true, isRequired: false, transformFunction: null }, paletteTitle: { classPropertyName: "paletteTitle", publicName: "paletteTitle", isSignal: true, isRequired: false, transformFunction: null }, inspectorTitle: { classPropertyName: "inspectorTitle", publicName: "inspectorTitle", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { schema: "schemaChange", fieldSelected: "fieldSelected", fieldAdded: "fieldAdded", fieldRemoved: "fieldRemoved" }, host: { classAttribute: "ngs-form-builder" }, viewQueries: [{ propertyName: "actualFieldsTree", first: true, predicate: ["actualFieldsTree"], descendants: true, isSignal: true }], exportAs: ["ngsFormBuilder"], ngImport: i0, template: "<ng-template #nativeFieldGhost>\n @if (nativeDragFieldDefinition(); as definition) {\n <div\n class=\"ngs-form-builder-field ngs-form-builder-field-placeholder ngs-form-builder-native-ghost-field\"\n [class.is-width-1]=\"definitionWidth(definition) === 1\"\n [class.is-width-2]=\"definitionWidth(definition) === 2\"\n [class.is-width-3]=\"definitionWidth(definition) === 3\"\n [class.is-width-4]=\"definitionWidth(definition) === 4\"\n [class.is-width-5]=\"definitionWidth(definition) === 5\"\n [class.is-width-6]=\"definitionWidth(definition) === 6\"\n [class.is-width-7]=\"definitionWidth(definition) === 7\"\n [class.is-width-8]=\"definitionWidth(definition) === 8\"\n [class.is-width-9]=\"definitionWidth(definition) === 9\"\n [class.is-width-10]=\"definitionWidth(definition) === 10\"\n [class.is-width-11]=\"definitionWidth(definition) === 11\"\n [class.is-width-12]=\"definitionWidth(definition) === 12\">\n <div class=\"ngs-form-builder-field-header\">\n <div class=\"ngs-form-builder-field-label\">\n <ngs-icon [name]=\"definition.icon || 'fluent:form-24-regular'\"/>\n <span>{{ definition.defaults?.label || definition.label }}</span>\n </div>\n <span class=\"ngs-form-builder-drag-icon\">\n <ngs-icon name=\"fluent:arrow-move-24-regular\"/>\n </span>\n </div>\n <div class=\"ngs-form-builder-ghost-control\"></div>\n </div>\n }\n</ng-template>\n\n<ng-template #nativeSectionGhost>\n <ngs-card class=\"ngs-form-builder-section ngs-form-builder-native-ghost-section\">\n <ngs-card-header>\n <div class=\"ngs-form-builder-section-heading\">\n <span class=\"ngs-form-builder-drag-icon\">\n <ngs-icon name=\"fluent:folder-add-24-regular\"/>\n </span>\n <span class=\"ngs-form-builder-section-title\">Section</span>\n </div>\n </ngs-card-header>\n </ngs-card>\n</ng-template>\n\n<ng-template #fieldList let-fields let-containerId=\"containerId\" let-section=\"section\">\n <div\n class=\"ngs-form-builder-drop-list ngs-form-builder-canvas-drop-list\"\n [id]=\"containerId\"\n (dragover)=\"nativeFieldDragOver($event, containerId)\"\n (dragleave)=\"nativeDragLeave($event, containerId)\"\n (drop)=\"nativeFieldDrop($event, containerId)\">\n @for (field of fields; track field.id; let fieldIndex = $index) {\n @if (isNativeDropTarget(containerId, fieldIndex)) {\n <ng-container [ngTemplateOutlet]=\"nativeFieldGhost\"/>\n }\n @if (isContainerField(field)) {\n <div\n class=\"ngs-form-builder-field is-container\"\n [class.is-width-1]=\"(field.width ?? 12) === 1\"\n [class.is-width-2]=\"field.width === 2\"\n [class.is-width-3]=\"field.width === 3\"\n [class.is-width-4]=\"field.width === 4\"\n [class.is-width-5]=\"field.width === 5\"\n [class.is-width-6]=\"field.width === 6\"\n [class.is-width-7]=\"field.width === 7\"\n [class.is-width-8]=\"field.width === 8\"\n [class.is-width-9]=\"field.width === 9\"\n [class.is-width-10]=\"field.width === 10\"\n [class.is-width-11]=\"field.width === 11\"\n [class.is-width-12]=\"(field.width ?? 12) === 12\"\n [class.is-selected]=\"selectedFieldId() === field.id\"\n [attr.data-form-builder-field-id]=\"field.id\"\n (dragover)=\"nativeContainerFieldDragOver($event, field)\"\n (dragleave)=\"nativeContainerFieldDragLeave($event, field)\"\n (drop)=\"nativeContainerFieldDrop($event, field)\"\n (click)=\"$event.stopPropagation(); selectCanvasField(field, section)\">\n <div class=\"ngs-form-builder-field-header\">\n <div class=\"ngs-form-builder-field-label\">\n <ngs-icon [name]=\"fieldIcon(field)\"/>\n <span>{{ field.label }}</span>\n </div>\n </div>\n\n <div class=\"ngs-form-builder-grid-field\">\n <ng-container\n [ngTemplateOutlet]=\"fieldList\"\n [ngTemplateOutletContext]=\"{\n $implicit: field.children ?? [],\n containerId: fieldDropListId(field),\n section\n }\"/>\n </div>\n </div>\n } @else {\n <div\n class=\"ngs-form-builder-field\"\n [class.is-width-1]=\"(field.width ?? 12) === 1\"\n [class.is-width-2]=\"field.width === 2\"\n [class.is-width-3]=\"field.width === 3\"\n [class.is-width-4]=\"field.width === 4\"\n [class.is-width-5]=\"field.width === 5\"\n [class.is-width-6]=\"field.width === 6\"\n [class.is-width-7]=\"field.width === 7\"\n [class.is-width-8]=\"field.width === 8\"\n [class.is-width-9]=\"field.width === 9\"\n [class.is-width-10]=\"field.width === 10\"\n [class.is-width-11]=\"field.width === 11\"\n [class.is-width-12]=\"(field.width ?? 12) === 12\"\n [class.is-selected]=\"selectedFieldId() === field.id\"\n [attr.data-form-builder-field-id]=\"field.id\"\n (click)=\"$event.stopPropagation(); selectCanvasField(field, section)\">\n <div class=\"ngs-form-builder-field-header\">\n <div class=\"ngs-form-builder-field-label\">\n <ngs-icon [name]=\"fieldIcon(field)\"/>\n <span>{{ field.label }}</span>\n </div>\n </div>\n\n <ngs-form-builder-field-host\n [field]=\"field\"\n [control]=\"previewControl(field)\"\n [definitions]=\"definitions()\"\n [readonly]=\"true\"\n [editableCanvas]=\"true\"/>\n </div>\n }\n }\n\n @if (isNativeDropTarget(containerId, fields.length)) {\n <ng-container [ngTemplateOutlet]=\"nativeFieldGhost\"/>\n }\n </div>\n</ng-template>\n\n<ngs-panel class=\"ngs-form-builder-shell\">\n <ngs-panel-header autoHeight class=\"flex items-center justify-between gap-4 border-b border-b-subtle px-5 py-3\">\n <div class=\"min-w-0\">\n <p class=\"text-sm text-secondary\">Form builder</p>\n <h2 class=\"truncate text-lg font-semibold\">{{ schema().title || 'Untitled form' }}</h2>\n </div>\n\n <div class=\"flex items-center gap-2\">\n <button ngsButton=\"outlined\" type=\"button\" (click)=\"openPreview(previewDialog)\">\n <ngs-icon name=\"fluent:eye-24-regular\"/>\n Preview\n </button>\n </div>\n </ngs-panel-header>\n\n <ngs-panel-sidebar class=\"ngs-form-builder-palette w-80 border-e border-e-subtle\">\n <ngs-tab-group\n animationDuration=\"0ms\"\n [selectedIndex]=\"fieldsTabIndex()\"\n (selectedIndexChange)=\"fieldsTabIndex.set($event)\">\n <ngs-tab label=\"Placeholders\" class=\"relative\">\n <ngs-scrollbar-area>\n <div class=\"flex flex-col gap-5 p-4\">\n <section class=\"flex flex-col gap-2\">\n <h4 class=\"text-xs font-semibold uppercase tracking-normal text-secondary\">Layout</h4>\n <button\n type=\"button\"\n class=\"ngs-form-builder-palette-item\"\n draggable=\"true\"\n (dragstart)=\"sectionPaletteDragStarted($event)\"\n (dragend)=\"paletteDragEnded()\">\n <span class=\"ngs-form-builder-palette-item-content\">\n <span class=\"ngs-form-builder-palette-item-label\">\n <ngs-icon name=\"fluent:folder-add-24-regular\"/>\n <span>Section</span>\n </span>\n </span>\n </button>\n\n @for (definition of layoutDefinitions(); track definition.type) {\n <button\n type=\"button\"\n class=\"ngs-form-builder-palette-item\"\n draggable=\"true\"\n (dragstart)=\"paletteDragStarted($event, definition)\"\n (dragend)=\"paletteDragEnded()\"\n (click)=\"paletteClicked(definition)\">\n <span class=\"ngs-form-builder-palette-item-content\">\n <span class=\"ngs-form-builder-palette-item-label\">\n <ngs-icon [name]=\"definition.icon || 'fluent:form-24-regular'\"/>\n <span>{{ definition.label }}</span>\n </span>\n </span>\n </button>\n }\n </section>\n\n @for (group of paletteGroups(); track group.name) {\n <section class=\"flex flex-col gap-2\">\n <h4 class=\"text-xs font-semibold uppercase tracking-normal text-secondary\">{{ group.name }}</h4>\n <div class=\"flex flex-col gap-2\">\n @for (definition of group.fields; track definition.type) {\n <button\n type=\"button\"\n class=\"ngs-form-builder-palette-item\"\n draggable=\"true\"\n (dragstart)=\"paletteDragStarted($event, definition)\"\n (dragend)=\"paletteDragEnded()\"\n (click)=\"paletteClicked(definition)\">\n <span class=\"ngs-form-builder-palette-item-content\">\n <span class=\"ngs-form-builder-palette-item-label\">\n <ngs-icon [name]=\"definition.icon || 'fluent:form-24-regular'\"/>\n <span>{{ definition.label }}</span>\n </span>\n </span>\n </button>\n }\n </div>\n </section>\n }\n </div>\n </ngs-scrollbar-area>\n </ngs-tab>\n <ngs-tab label=\"Actual fields\" class=\"relative\">\n <ngs-scrollbar-area>\n <div class=\"ps-1 pe-4 py-4\">\n <ngs-tree\n #actualFieldsTree=\"ngsTree\"\n [dataSource]=\"fieldTree()\"\n [childrenAccessor]=\"fieldTreeChildrenAccessor\"\n [trackBy]=\"trackFieldTreeNode\"\n [draggablePredicate]=\"fieldTreeDraggablePredicate\"\n [dropPredicate]=\"fieldTreeDropPredicate\"\n [nodePaddingIndent]=\"80\"\n [reorderOnDrop]=\"false\"\n (nodeDrop)=\"fieldTreeNodeDropped($event)\"\n draggable>\n <ng-template ngsTreeDragPlaceholder let-source=\"source\">\n <div class=\"ngs-form-builder-tree-drag-placeholder\">\n <ngs-icon [name]=\"fieldTreePlaceholderIcon(source)\"/>\n <span>{{ source.label }}</span>\n </div>\n </ng-template>\n\n <ngs-tree-node *ngsTreeNodeDef=\"let node\" ngsTreeNodePadding [value]=\"node.id\">\n <button\n ngsButton=\"text\"\n type=\"button\"\n class=\"ngs-form-builder-tree-item\"\n [class.is-selected]=\"selectedFieldId() === node.id\"\n [attr.data-form-builder-tree-node-id]=\"node.id\"\n (click)=\"selectFieldTreeNode(node)\">\n <ngs-icon [name]=\"node.icon\"/>\n {{ node.label }}\n<!-- <span class=\"inline-flex flex-col\">-->\n<!-- -->\n<!-- @if (node.name) {-->\n<!-- <small>{{ node.name }}</small>-->\n<!-- }-->\n<!-- </span>-->\n </button>\n </ngs-tree-node>\n\n <ngs-tree-node *ngsTreeNodeDef=\"let node; when: hasFieldTreeChildren\"\n ngsTreeNodePadding\n [value]=\"node.id\"\n [cdkTreeNodeTypeaheadLabel]=\"node.label\">\n <button\n ngsIconButton\n class=\"ngs-form-builder-tree-toggle\"\n [attr.aria-label]=\"'Toggle ' + node.label\"\n (click)=\"toggleFieldTreeNode(actualFieldsTree, node, $event)\">\n <ngs-icon [name]=\"isFieldTreeNodeExpanded(actualFieldsTree, node) ? 'fluent:chevron-down-24-regular' : 'fluent:chevron-right-24-regular'\" class=\"size-4\"/>\n </button>\n <button\n ngsButton=\"text\"\n type=\"button\"\n class=\"ngs-form-builder-tree-item\"\n [class.is-selected]=\"selectedFieldId() === node.id\"\n [attr.data-form-builder-tree-node-id]=\"node.id\"\n (click)=\"selectFieldTreeNode(node)\">\n <ngs-icon [name]=\"node.icon\"/>\n <span>{{ node.label }}</span>\n <!-- @if (node.name) {-->\n <!-- <small>{{ node.name }}</small>-->\n <!-- }-->\n </button>\n </ngs-tree-node>\n </ngs-tree>\n </div>\n </ngs-scrollbar-area>\n </ngs-tab>\n </ngs-tab-group>\n </ngs-panel-sidebar>\n\n <ngs-panel-content>\n <ngs-scrollbar-area [absolute]=\"true\">\n <div class=\"ngs-form-builder-canvas flex flex-col gap-4 p-5\">\n <div\n class=\"ngs-form-builder-canvas-list\"\n [id]=\"rootDropListId()\"\n (dragover)=\"nativeCanvasDragOver($event)\"\n (dragleave)=\"nativeDragLeave($event, rootDropListId())\"\n (drop)=\"nativeFieldDrop($event, rootDropListId())\">\n @for (item of canvasItems(); track item.kind + ':' + item.id; let itemIndex = $index) {\n @if (isNativeDropTarget(rootDropListId(), itemIndex)) {\n <ng-container [ngTemplateOutlet]=\"nativeDragSection() ? nativeSectionGhost : nativeFieldGhost\"/>\n }\n @if (item.field; as field) {\n @if (isContainerField(field)) {\n <div\n class=\"ngs-form-builder-field is-container\"\n [class.is-width-1]=\"(field.width ?? 12) === 1\"\n [class.is-width-2]=\"field.width === 2\"\n [class.is-width-3]=\"field.width === 3\"\n [class.is-width-4]=\"field.width === 4\"\n [class.is-width-5]=\"field.width === 5\"\n [class.is-width-6]=\"field.width === 6\"\n [class.is-width-7]=\"field.width === 7\"\n [class.is-width-8]=\"field.width === 8\"\n [class.is-width-9]=\"field.width === 9\"\n [class.is-width-10]=\"field.width === 10\"\n [class.is-width-11]=\"field.width === 11\"\n [class.is-width-12]=\"(field.width ?? 12) === 12\"\n [class.is-selected]=\"selectedFieldId() === field.id\"\n [attr.data-form-builder-field-id]=\"field.id\"\n (dragover)=\"nativeContainerFieldDragOver($event, field)\"\n (dragleave)=\"nativeContainerFieldDragLeave($event, field)\"\n (drop)=\"nativeContainerFieldDrop($event, field)\"\n (click)=\"$event.stopPropagation(); selectCanvasField(field)\">\n <div class=\"ngs-form-builder-field-header\">\n <div class=\"ngs-form-builder-field-label\">\n <ngs-icon [name]=\"fieldIcon(field)\"/>\n <span>{{ field.label }}</span>\n </div>\n </div>\n\n <div class=\"ngs-form-builder-grid-field\">\n <ng-container\n [ngTemplateOutlet]=\"fieldList\"\n [ngTemplateOutletContext]=\"{\n $implicit: field.children ?? [],\n containerId: fieldDropListId(field)\n }\"/>\n </div>\n </div>\n } @else {\n <div\n class=\"ngs-form-builder-field\"\n [class.is-width-1]=\"(field.width ?? 12) === 1\"\n [class.is-width-2]=\"field.width === 2\"\n [class.is-width-3]=\"field.width === 3\"\n [class.is-width-4]=\"field.width === 4\"\n [class.is-width-5]=\"field.width === 5\"\n [class.is-width-6]=\"field.width === 6\"\n [class.is-width-7]=\"field.width === 7\"\n [class.is-width-8]=\"field.width === 8\"\n [class.is-width-9]=\"field.width === 9\"\n [class.is-width-10]=\"field.width === 10\"\n [class.is-width-11]=\"field.width === 11\"\n [class.is-width-12]=\"(field.width ?? 12) === 12\"\n [class.is-selected]=\"selectedFieldId() === field.id\"\n [attr.data-form-builder-field-id]=\"field.id\"\n (click)=\"$event.stopPropagation(); selectCanvasField(field)\">\n <div class=\"ngs-form-builder-field-header\">\n <div class=\"ngs-form-builder-field-label\">\n <ngs-icon [name]=\"fieldIcon(field)\"/>\n <span>{{ field.label }}</span>\n </div>\n </div>\n\n <ngs-form-builder-field-host\n [field]=\"field\"\n [control]=\"previewControl(field)\"\n [definitions]=\"definitions()\"\n [readonly]=\"true\"\n [editableCanvas]=\"true\"/>\n </div>\n }\n } @else if (item.section; as section) {\n <ngs-card class=\"ngs-form-builder-section\"\n [attr.data-form-builder-section-id]=\"section.id\"\n (dragover)=\"nativeSectionDragOver($event, section)\"\n (dragleave)=\"nativeDragLeave($event, sectionDropListId(section))\"\n (drop)=\"nativeSectionDrop($event, section)\"\n (click)=\"selectSection(section)\">\n <ngs-card-header>\n <div class=\"ngs-form-builder-section-heading\">\n <input ngsInput\n class=\"ngs-form-builder-section-title\"\n [ngModel]=\"section.title\"\n [ngModelOptions]=\"{standalone: true}\"\n (ngModelChange)=\"updateSection(section, { title: $event })\"\n aria-label=\"Section title\">\n </div>\n @if (section.description) {\n <p class=\"truncate text-sm text-secondary\">{{ section.description }}</p>\n }\n\n <ngs-card-aside>\n <div class=\"ngs-form-builder-section-actions\">\n <button ngsIconButton type=\"button\" aria-label=\"Collapse section\" (click)=\"updateSection(section, { collapsed: !section.collapsed })\">\n <ngs-icon [name]=\"isSectionCollapsed(section) ? 'fluent:chevron-down-24-regular' : 'fluent:chevron-up-24-regular'\"/>\n </button>\n <button ngsIconButton type=\"button\" aria-label=\"Delete section\" (click)=\"removeSection(section)\">\n <ngs-icon name=\"fluent:delete-24-regular\"/>\n </button>\n </div>\n </ngs-card-aside>\n </ngs-card-header>\n\n @if (!isSectionCollapsed(section)) {\n <ngs-card-content>\n <ng-container\n [ngTemplateOutlet]=\"fieldList\"\n [ngTemplateOutletContext]=\"{\n $implicit: section.fields,\n containerId: sectionDropListId(section),\n section\n }\"/>\n </ngs-card-content>\n } @else if (isNativeDropTarget(sectionDropListId(section), section.fields.length)) {\n <ngs-card-content>\n <div class=\"ngs-form-builder-drop-list ngs-form-builder-canvas-drop-list\">\n <ng-container [ngTemplateOutlet]=\"nativeFieldGhost\"/>\n </div>\n </ngs-card-content>\n }\n </ngs-card>\n }\n }\n\n @if (isNativeDropTarget(rootDropListId(), canvasItems().length)) {\n <ng-container [ngTemplateOutlet]=\"nativeDragSection() ? nativeSectionGhost : nativeFieldGhost\"/>\n }\n\n </div>\n </div>\n </ngs-scrollbar-area>\n </ngs-panel-content>\n\n <ngs-panel-aside class=\"ngs-form-builder-inspector w-88 border-s border-s-subtle\">\n <ngs-scrollbar-area [absolute]=\"true\">\n <div class=\"flex flex-col gap-4 p-5\">\n <div class=\"flex items-start justify-between gap-3\">\n <div class=\"min-w-0\">\n <h3 class=\"text-base font-semibold\">{{ inspectorTitle() }}</h3>\n @if (selectedField() || selectedSection()) {\n <p class=\"truncate text-sm text-secondary\">{{ selectedField()?.label || selectedSection()?.title }}</p>\n }\n </div>\n <button ngsIconButton type=\"button\" aria-label=\"Clear selection\" (click)=\"selectedFieldId.set(null)\">\n <ngs-icon name=\"fluent:dismiss-24-regular\"/>\n </button>\n </div>\n\n @if (selectedField() || selectedSection()) {\n <ngs-form-builder-settings-host\n [field]=\"selectedField()\"\n [section]=\"selectedSection()\"\n [schema]=\"schema()\"\n [definitions]=\"definitions()\"\n [settingsDefinitions]=\"settingsDefinitions()\"\n [update]=\"updateSelectedField\"\n [updateSection]=\"updateSelectedSection\"/>\n\n @if (selectedField(); as field) {\n <button ngsButton=\"text\" type=\"button\" class=\"ngs-form-builder-delete-button\" (click)=\"confirmRemoveField(field)\">\n <ngs-icon name=\"fluent:delete-24-regular\"/>\n Delete field\n </button>\n }\n } @else {\n <div class=\"ngs-form-builder-empty-inspector\">\n Select a field on the canvas or in the fields tree to edit its settings.\n </div>\n }\n </div>\n </ngs-scrollbar-area>\n </ngs-panel-aside>\n</ngs-panel>\n\n<ng-template #previewDialog>\n <h3 ngs-dialog-title>{{ schema().title || 'Form preview' }}</h3>\n <ngs-dialog-content class=\"ngs-form-builder-preview-dialog-content\">\n <ngs-form-builder-renderer\n [schema]=\"schema()\"\n [showSubmit]=\"false\"/>\n </ngs-dialog-content>\n <ngs-dialog-actions align=\"end\">\n <button ngsButton=\"outlined\" type=\"button\" ngs-dialog-close>Close</button>\n </ngs-dialog-actions>\n</ng-template>\n", styles: [":host{display:block;min-height:640px}:host .ngs-form-builder-shell{height:100%;min-height:640px;overflow:hidden}:host .ngs-form-builder-palette-item{display:flex;width:100%;min-height:calc(var(--spacing, .25rem) * 12);align-items:center;border:1px solid var(--ngs-color-outline-variant);border-radius:var(--ngs-radius-lg);background:var(--ngs-color-surface);color:var(--ngs-color-on-surface);cursor:grab;font-size:var(--ngs-font-size-sm);font-weight:500;padding:0 calc(var(--spacing, .25rem) * 3);text-align:start;transition:background-color .16s cubic-bezier(0,0,.2,1),border-color .16s cubic-bezier(0,0,.2,1),box-shadow .16s cubic-bezier(0,0,.2,1)}:host .ngs-form-builder-palette-item:hover{border-color:var(--ngs-color-primary);background:var(--ngs-color-primary);color:var(--ngs-color-primary)}@supports (color: color-mix(in lab,red,red)){:host .ngs-form-builder-palette-item:hover{background:color-mix(in srgb,var(--ngs-color-primary),var(--ngs-color-surface) 96%)}}:host .ngs-form-builder-palette-item:focus-visible{outline:0;box-shadow:0 0 0 3px var(--ngs-state-focus-ring)}:host .ngs-form-builder-palette-item:active{cursor:grabbing}:host .ngs-form-builder-palette-item-preview{width:calc(var(--spacing, .25rem) * 64)}:host .ngs-form-builder-native-drag-image{position:fixed;inset-block-start:0;inset-inline-start:0;width:calc(var(--spacing, .25rem) * 64);pointer-events:none;transform:translate(-200vw,-200vh);z-index:-1}:host .ngs-form-builder-palette-item-placeholder{width:100%;box-shadow:none;opacity:1;pointer-events:none}:host .ngs-form-builder-palette{background:var(--ngs-color-surface-container-low)}:host .ngs-form-builder-palette-item-content{display:inline-flex;min-width:0;width:100%;align-items:center;justify-content:space-between;gap:calc(var(--spacing, .25rem) * 3)}:host .ngs-form-builder-palette-item-label{display:inline-flex;min-width:0;align-items:center;gap:calc(var(--spacing, .25rem) * 2)}:host .ngs-form-builder-palette-item-label span{min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}:host ngs-tab-group{--ngs-tab-label-height: calc(var(--spacing, .25rem) * 10)}:host ngs-tree{--ngs-tree-node-gap: calc(var(--spacing, .25rem) * 1)}:host ngs-tree ngs-tree-node{cursor:grab}:host ngs-tree ngs-tree-node:active{cursor:grabbing}:host .ngs-form-builder-tree-item{min-width:0;flex:1;justify-content:flex-start;overflow:hidden}:host .ngs-form-builder-tree-item span{min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}:host .ngs-form-builder-tree-item small{margin-inline-start:auto;color:var(--ngs-color-on-surface-variant);font-size:var(--ngs-font-size-xs);font-weight:400}:host .ngs-form-builder-tree-item.is-selected{background:var(--ngs-color-primary-container);color:var(--ngs-color-on-primary-container)}:host .ngs-form-builder-tree-item.is-selected small{color:currentColor}:host .ngs-form-builder-tree-drag-placeholder{display:flex;min-width:0;width:100%;min-height:calc(var(--spacing, .25rem) * 10);align-items:center;gap:calc(var(--spacing, .25rem) * 2);border:1px dashed var(--ngs-color-primary);border-radius:var(--ngs-radius-md);background:var(--ngs-color-primary);color:var(--ngs-color-primary);font-size:var(--ngs-font-size-sm);font-weight:500;padding:calc(var(--spacing, .25rem) * 2) calc(var(--spacing, .25rem) * 3);pointer-events:none}@supports (color: color-mix(in lab,red,red)){:host .ngs-form-builder-tree-drag-placeholder{background:color-mix(in srgb,var(--ngs-color-primary),var(--ngs-color-surface) 94%)}}:host .ngs-form-builder-tree-drag-placeholder ngs-icon{flex:none}:host .ngs-form-builder-tree-drag-placeholder span{min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}:host .ngs-form-builder-drag-icon{display:inline-flex;flex:none;align-items:center;color:var(--ngs-color-on-surface-variant)}:host .ngs-form-builder-canvas{min-height:100%}:host .ngs-form-builder-section{--ngs-card-padding: calc(var(--spacing, .25rem) * 4)}:host .ngs-form-builder-native-ghost-section{border-color:var(--ngs-color-primary);background:var(--ngs-color-primary);box-shadow:none;opacity:1;pointer-events:none}@supports (color: color-mix(in lab,red,red)){:host .ngs-form-builder-native-ghost-section{background:color-mix(in srgb,var(--ngs-color-primary),var(--ngs-color-surface) 94%)}}:host .ngs-form-builder-canvas-list{display:flex;flex-direction:column;gap:calc(var(--spacing, .25rem) * 4)}:host .ngs-form-builder-section-heading{display:flex;min-width:0;flex:1;align-items:center;gap:calc(var(--spacing, .25rem) * 3)}:host .ngs-form-builder-section-actions{display:flex;align-items:center;gap:calc(var(--spacing, .25rem) * 1)}:host .ngs-form-builder-section-title{width:100%;min-width:0;height:auto;min-height:0;padding:0;border:0;background:transparent;color:var(--ngs-color-on-surface);font-size:var(--ngs-font-size-base);font-weight:600;line-height:var(--ngs-line-height-base);outline:0}:host .ngs-form-builder-section-title:focus{outline:2px solid var(--ngs-color-primary);outline-offset:2px;border-radius:var(--ngs-radius-sm)}:host .ngs-form-builder-section-preview-title{min-width:0;overflow:hidden;color:var(--ngs-color-on-surface);font-size:var(--ngs-font-size-base);font-weight:600;line-height:var(--ngs-line-height-base);text-overflow:ellipsis;white-space:nowrap}:host .ngs-form-builder-drop-list{display:grid;grid-template-columns:repeat(12,minmax(0,1fr));gap:calc(var(--spacing, .25rem) * 4);min-height:calc(var(--spacing, .25rem) * 16)}:host .ngs-form-builder-field{position:relative;display:flex;flex-direction:column;grid-column:span 12;gap:calc(var(--spacing, .25rem) * 3);border:1px solid transparent;border-radius:var(--ngs-radius-lg);padding:calc(var(--spacing, .25rem) * 3);background:var(--ngs-color-surface);cursor:pointer}:host .ngs-form-builder-field.is-container{align-items:stretch;background:var(--ngs-color-surface-container-lowest)}:host .ngs-form-builder-field.is-width-1{grid-column:span 1}:host .ngs-form-builder-field.is-width-2{grid-column:span 2}:host .ngs-form-builder-field.is-width-3{grid-column:span 3}:host .ngs-form-builder-field.is-width-4{grid-column:span 4}:host .ngs-form-builder-field.is-width-5{grid-column:span 5}:host .ngs-form-builder-field.is-width-6{grid-column:span 6}:host .ngs-form-builder-field.is-width-7{grid-column:span 7}:host .ngs-form-builder-field.is-width-8{grid-column:span 8}:host .ngs-form-builder-field.is-width-9{grid-column:span 9}:host .ngs-form-builder-field.is-width-10{grid-column:span 10}:host .ngs-form-builder-field.is-width-11{grid-column:span 11}:host .ngs-form-builder-field.is-width-12{grid-column:span 12}:host .ngs-form-builder-field:hover{border-color:var(--ngs-color-outline-variant)}:host .ngs-form-builder-field.is-selected{border-color:var(--ngs-color-primary);box-shadow:0 0 0 1px var(--ngs-color-primary)}:host .ngs-form-builder-field-header{display:flex;min-width:0;align-items:center;justify-content:space-between;gap:calc(var(--spacing, .25rem) * 3)}:host .ngs-form-builder-field-label{display:flex;min-width:0;align-items:center;gap:calc(var(--spacing, .25rem) * 2);color:var(--ngs-color-on-surface);font-size:var(--ngs-font-size-sm);font-weight:600}:host .ngs-form-builder-field-label ngs-icon{flex:none;color:var(--ngs-color-primary)}:host .ngs-form-builder-field-label span{min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}:host .ngs-form-builder-field-host{min-width:0;width:100%;grid-column:auto}:host .ngs-form-builder-grid-field{display:flex;min-width:0;flex-direction:column;gap:calc(var(--spacing, .25rem) * 3)}:host .ngs-form-builder-grid-field-header{display:flex;align-items:center;justify-content:space-between;gap:calc(var(--spacing, .25rem) * 3);border:1px solid var(--ngs-color-outline-variant);border-radius:var(--ngs-radius-md);background:var(--ngs-color-surface);padding:calc(var(--spacing, .25rem) * 3)}:host .ngs-form-builder-grid-field-header p{overflow:hidden;color:var(--ngs-color-on-surface);font-size:var(--ngs-font-size-sm);font-weight:600;text-overflow:ellipsis;white-space:nowrap}:host .ngs-form-builder-grid-field-header span{display:block;overflow:hidden;color:var(--ngs-color-on-surface-variant);font-size:var(--ngs-font-size-xs);text-overflow:ellipsis;white-space:nowrap}:host .ngs-form-builder-empty-inspector{display:flex;min-height:calc(var(--spacing, .25rem) * 14);align-items:center;justify-content:center;border:1px dashed var(--ngs-color-primary);border-radius:var(--ngs-radius-lg);color:var(--ngs-color-on-surface-variant);font-size:var(--ngs-font-size-sm);grid-column:1/-1;padding:calc(var(--spacing, .25rem) * 4);text-align:center}:host .ngs-form-builder-empty-inspector{min-height:calc(var(--spacing, .25rem) * 28);border-color:var(--ngs-color-outline-variant)}:host .ngs-form-builder-delete-button{justify-content:flex-start;color:var(--ngs-color-danger)}:host .ngs-form-builder-preview-dialog-content{width:min(100%,760px)}:host .ngs-form-builder-field-placeholder{border-style:dashed;border-color:var(--ngs-color-primary);background:var(--ngs-color-primary);box-shadow:none;opacity:1;pointer-events:none}@supports (color: color-mix(in lab,red,red)){:host .ngs-form-builder-field-placeholder{background:color-mix(in srgb,var(--ngs-color-primary),var(--ngs-color-surface) 92%)}}:host .ngs-form-builder-ghost-control{height:calc(var(--spacing, .25rem) * 10);border:1px solid var(--ngs-color-outline-variant);border-radius:var(--ngs-radius-md);background:var(--ngs-color-surface)}\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: 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: 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: Card, selector: "ngs-card", inputs: ["appearance"], exportAs: ["ngsCard"] }, { kind: "component", type: CardAside, selector: "ngs-card-aside, [ngs-card-aside], ngsCardAside" }, { kind: "component", type: CardContent, selector: "ngs-card-content, [ngs-card-content], [ngsCardContent]", inputs: ["withoutPadding"], exportAs: ["ngsCardContent"] }, { kind: "component", type: CardHeader, selector: "ngs-card-header", exportAs: ["ngsCardHeader"] }, { kind: "component", type: DialogActions, selector: "ngs-dialog-actions, [ngs-dialog-actions], [ngsDialogActions]", inputs: ["align"] }, { kind: "directive", type: DialogClose, selector: "[ngs-dialog-close], [ngsDialogClose]", inputs: ["ngs-dialog-close", "ngsDialogClose", "ariaLabel", "type"], exportAs: ["ngsDialogClose"] }, { kind: "component", type: DialogContent, selector: "ngs-dialog-content,[ngs-dialog-content],[ngsDialogContent]" }, { kind: "component", type: DialogTitle, selector: "ngs-dialog-title, [ngs-dialog-title], [ngsDialogTitle]", inputs: ["id"], exportAs: ["ngsDialogTitle"] }, { kind: "component", type: Icon, selector: "ngs-icon", inputs: ["name"], exportAs: ["ngsIcon"] }, { kind: "directive", type: Input, selector: "input[ngsInput], textarea[ngsInput]", inputs: ["id", "placeholder", "required", "disabled", "readonly", "errorStateMatcher"], exportAs: ["ngsInput"] }, { kind: "component", type: Panel, selector: "ngs-panel", inputs: ["absolute"], exportAs: ["ngsPanel"] }, { kind: "component", type: PanelAside, selector: "ngs-panel-aside" }, { kind: "component", type: PanelContent, selector: "ngs-panel-content", exportAs: ["ngsPanelContent"] }, { kind: "component", type: PanelHeader, selector: "ngs-panel-header", inputs: ["flex", "autoHeight"], exportAs: ["ngsPanelHeader"] }, { kind: "component", type: PanelSidebar, selector: "ngs-panel-sidebar" }, { kind: "component", type: ScrollbarArea, selector: "ngs-scrollbar-area", inputs: ["scrollbarWidth", "autoHide", "absolute"], outputs: ["scrolled"], exportAs: ["ngsScrollbarArea"] }, { kind: "component", type: Tab, selector: "ngs-tab", inputs: ["label", "aria-label", "aria-labelledby", "disabled"], exportAs: ["ngsTab"] }, { kind: "component", type: TabGroup, selector: "ngs-tab-group", inputs: ["selectedIndex", "headerPosition", "preserveContent", "ngs-stretch-tabs", "ngs-align-tabs", "disableRipple", "animationDuration", "animate.enter", "animate.leave"], outputs: ["selectedIndexChange", "selectedTabChange", "focusChange"] }, { kind: "component", type: Tree, selector: "ngs-tree", inputs: ["checkable", "selectable", "draggable", "draggablePredicate", "dropPredicate", "reorderOnDrop", "dragPreview", "nodePaddingIndent", "childrenKey", "filterValue", "filterPredicate", "filterMode"], outputs: ["checkedChange", "selectedChange", "nodeDrop"], exportAs: ["ngsTree"] }, { kind: "directive", type: TreeDragPlaceholder, selector: "ng-template[ngsTreeDragPlaceholder]" }, { kind: "component", type: TreeNode, selector: "ngs-tree-node", inputs: ["tabIndex", "value", "disabled"], outputs: ["activation", "expandedChange"], exportAs: ["ngsTreeNode"] }, { kind: "directive", type: TreeNodeDef, selector: "[ngsTreeNodeDef]", inputs: ["ngsTreeNodeDefWhen", "ngsTreeNode"] }, { kind: "directive", type: TreeNodePadding, selector: "[ngsTreeNodePadding]", inputs: ["ngsTreeNodePadding", "ngsTreeNodePaddingIndent"] }, { kind: "component", type: FormBuilderFieldHost, selector: "ngs-form-builder-field-host", inputs: ["field", "control", "definitions", "readonly", "editableCanvas"], exportAs: ["ngsFormBuilderFieldHost"] }, { kind: "component", type: FormBuilderRenderer, selector: "ngs-form-builder-renderer", inputs: ["schema", "readonly", "showSubmit", "submitLabel", "value"], outputs: ["valueChange", "formSubmit", "formReady"], exportAs: ["ngsFormBuilderRenderer"] }, { kind: "component", type: FormBuilderSettingsHost, selector: "ngs-form-builder-settings-host", inputs: ["field", "section", "schema", "definitions", "settingsDefinitions", "update", "updateSection"], exportAs: ["ngsFormBuilderSettingsHost"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
2169
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.4", type: FormBuilder, isStandalone: true, selector: "ngs-form-builder", inputs: { schema: { classPropertyName: "schema", publicName: "schema", isSignal: true, isRequired: false, transformFunction: null }, paletteTitle: { classPropertyName: "paletteTitle", publicName: "paletteTitle", isSignal: true, isRequired: false, transformFunction: null }, inspectorTitle: { classPropertyName: "inspectorTitle", publicName: "inspectorTitle", isSignal: true, isRequired: false, transformFunction: null }, uploadCallback: { classPropertyName: "uploadCallback", publicName: "uploadCallback", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { schema: "schemaChange", fieldSelected: "fieldSelected", fieldAdded: "fieldAdded", fieldRemoved: "fieldRemoved" }, host: { classAttribute: "ngs-form-builder" }, viewQueries: [{ propertyName: "actualFieldsTree", first: true, predicate: ["actualFieldsTree"], descendants: true, isSignal: true }], exportAs: ["ngsFormBuilder"], ngImport: i0, template: "<ng-template #nativeFieldGhost>\n @if (nativeDragFieldDefinition(); as definition) {\n <div\n class=\"ngs-form-builder-field ngs-form-builder-field-placeholder ngs-form-builder-native-ghost-field\"\n [class.is-width-1]=\"definitionWidth(definition) === 1\"\n [class.is-width-2]=\"definitionWidth(definition) === 2\"\n [class.is-width-3]=\"definitionWidth(definition) === 3\"\n [class.is-width-4]=\"definitionWidth(definition) === 4\"\n [class.is-width-5]=\"definitionWidth(definition) === 5\"\n [class.is-width-6]=\"definitionWidth(definition) === 6\"\n [class.is-width-7]=\"definitionWidth(definition) === 7\"\n [class.is-width-8]=\"definitionWidth(definition) === 8\"\n [class.is-width-9]=\"definitionWidth(definition) === 9\"\n [class.is-width-10]=\"definitionWidth(definition) === 10\"\n [class.is-width-11]=\"definitionWidth(definition) === 11\"\n [class.is-width-12]=\"definitionWidth(definition) === 12\">\n <div class=\"ngs-form-builder-field-header\">\n <div class=\"ngs-form-builder-field-label\">\n <ngs-icon [name]=\"definition.icon || 'fluent:form-24-regular'\"/>\n <span>{{ definition.defaults?.label || definition.label }}</span>\n </div>\n <span class=\"ngs-form-builder-drag-icon\">\n <ngs-icon name=\"fluent:arrow-move-24-regular\"/>\n </span>\n </div>\n <div class=\"ngs-form-builder-ghost-control\"></div>\n </div>\n }\n</ng-template>\n\n<ng-template #nativeSectionGhost>\n <ngs-card class=\"ngs-form-builder-section ngs-form-builder-native-ghost-section\">\n <ngs-card-header>\n <div class=\"ngs-form-builder-section-heading\">\n <span class=\"ngs-form-builder-drag-icon\">\n <ngs-icon name=\"fluent:folder-add-24-regular\"/>\n </span>\n <span class=\"ngs-form-builder-section-title\">Section</span>\n </div>\n </ngs-card-header>\n </ngs-card>\n</ng-template>\n\n<ng-template #fieldList let-fields let-containerId=\"containerId\" let-section=\"section\">\n <div\n class=\"ngs-form-builder-drop-list ngs-form-builder-canvas-drop-list\"\n [id]=\"containerId\"\n (dragover)=\"nativeFieldDragOver($event, containerId)\"\n (dragleave)=\"nativeDragLeave($event, containerId)\"\n (drop)=\"nativeFieldDrop($event, containerId)\">\n @for (field of fields; track field.id; let fieldIndex = $index) {\n @if (isNativeDropTarget(containerId, fieldIndex)) {\n <ng-container [ngTemplateOutlet]=\"nativeFieldGhost\"/>\n }\n @if (isContainerField(field)) {\n <div\n class=\"ngs-form-builder-field is-container\"\n [class.is-width-1]=\"(field.width ?? 12) === 1\"\n [class.is-width-2]=\"field.width === 2\"\n [class.is-width-3]=\"field.width === 3\"\n [class.is-width-4]=\"field.width === 4\"\n [class.is-width-5]=\"field.width === 5\"\n [class.is-width-6]=\"field.width === 6\"\n [class.is-width-7]=\"field.width === 7\"\n [class.is-width-8]=\"field.width === 8\"\n [class.is-width-9]=\"field.width === 9\"\n [class.is-width-10]=\"field.width === 10\"\n [class.is-width-11]=\"field.width === 11\"\n [class.is-width-12]=\"(field.width ?? 12) === 12\"\n [class.is-selected]=\"selectedFieldId() === field.id\"\n [attr.data-form-builder-field-id]=\"field.id\"\n (dragover)=\"nativeContainerFieldDragOver($event, field)\"\n (dragleave)=\"nativeContainerFieldDragLeave($event, field)\"\n (drop)=\"nativeContainerFieldDrop($event, field)\"\n (click)=\"$event.stopPropagation(); selectCanvasField(field, section)\">\n <div class=\"ngs-form-builder-field-header\">\n <div class=\"ngs-form-builder-field-label\">\n <ngs-icon [name]=\"fieldIcon(field)\"/>\n <span>{{ field.label }}</span>\n </div>\n </div>\n\n <div class=\"ngs-form-builder-grid-field\">\n <ng-container\n [ngTemplateOutlet]=\"fieldList\"\n [ngTemplateOutletContext]=\"{\n $implicit: field.children ?? [],\n containerId: fieldDropListId(field),\n section\n }\"/>\n </div>\n </div>\n } @else {\n <div\n class=\"ngs-form-builder-field\"\n [class.is-width-1]=\"(field.width ?? 12) === 1\"\n [class.is-width-2]=\"field.width === 2\"\n [class.is-width-3]=\"field.width === 3\"\n [class.is-width-4]=\"field.width === 4\"\n [class.is-width-5]=\"field.width === 5\"\n [class.is-width-6]=\"field.width === 6\"\n [class.is-width-7]=\"field.width === 7\"\n [class.is-width-8]=\"field.width === 8\"\n [class.is-width-9]=\"field.width === 9\"\n [class.is-width-10]=\"field.width === 10\"\n [class.is-width-11]=\"field.width === 11\"\n [class.is-width-12]=\"(field.width ?? 12) === 12\"\n [class.is-selected]=\"selectedFieldId() === field.id\"\n [attr.data-form-builder-field-id]=\"field.id\"\n (click)=\"$event.stopPropagation(); selectCanvasField(field, section)\">\n <div class=\"ngs-form-builder-field-header\">\n <div class=\"ngs-form-builder-field-label\">\n <ngs-icon [name]=\"fieldIcon(field)\"/>\n <span>{{ field.label }}</span>\n </div>\n </div>\n\n <ngs-form-builder-field-host\n [field]=\"field\"\n [control]=\"previewControl(field)\"\n [definitions]=\"definitions()\"\n [uploadCallback]=\"uploadCallback()\"\n [readonly]=\"true\"\n [editableCanvas]=\"true\"/>\n </div>\n }\n }\n\n @if (isNativeDropTarget(containerId, fields.length)) {\n <ng-container [ngTemplateOutlet]=\"nativeFieldGhost\"/>\n }\n </div>\n</ng-template>\n\n<ngs-panel class=\"ngs-form-builder-shell\">\n <ngs-panel-header autoHeight class=\"flex items-center justify-between gap-4 border-b border-b-subtle px-5 py-3\">\n <div class=\"min-w-0\">\n <p class=\"text-sm text-secondary\">Form builder</p>\n <h2 class=\"truncate text-lg font-semibold\">{{ schema().title || 'Untitled form' }}</h2>\n </div>\n\n <div class=\"flex items-center gap-2\">\n <button ngsButton=\"outlined\" type=\"button\" (click)=\"openPreview(previewDialog)\">\n <ngs-icon name=\"fluent:eye-24-regular\"/>\n Preview\n </button>\n </div>\n </ngs-panel-header>\n\n <ngs-panel-sidebar class=\"ngs-form-builder-palette w-80 border-e border-e-subtle\">\n <ngs-tab-group\n animationDuration=\"0ms\"\n [selectedIndex]=\"fieldsTabIndex()\"\n (selectedIndexChange)=\"fieldsTabIndex.set($event)\">\n <ngs-tab label=\"Fields\" class=\"relative\">\n <ngs-scrollbar-area>\n <div class=\"flex flex-col gap-5 p-4\">\n <section class=\"flex flex-col gap-2\">\n <h4 class=\"text-xs font-semibold uppercase tracking-normal text-secondary\">Layout</h4>\n <button\n type=\"button\"\n class=\"ngs-form-builder-palette-item\"\n draggable=\"true\"\n (dragstart)=\"sectionPaletteDragStarted($event)\"\n (dragend)=\"paletteDragEnded()\">\n <span class=\"ngs-form-builder-palette-item-content\">\n <span class=\"ngs-form-builder-palette-item-label\">\n <ngs-icon name=\"fluent:folder-add-24-regular\"/>\n <span>Section</span>\n </span>\n </span>\n </button>\n\n @for (definition of layoutDefinitions(); track definition.type) {\n <button\n type=\"button\"\n class=\"ngs-form-builder-palette-item\"\n draggable=\"true\"\n (dragstart)=\"paletteDragStarted($event, definition)\"\n (dragend)=\"paletteDragEnded()\"\n (click)=\"paletteClicked(definition)\">\n <span class=\"ngs-form-builder-palette-item-content\">\n <span class=\"ngs-form-builder-palette-item-label\">\n <ngs-icon [name]=\"definition.icon || 'fluent:form-24-regular'\"/>\n <span>{{ definition.label }}</span>\n </span>\n </span>\n </button>\n }\n </section>\n\n @for (group of paletteGroups(); track group.name) {\n <section class=\"flex flex-col gap-2\">\n <h4 class=\"text-xs font-semibold uppercase tracking-normal text-secondary\">{{ group.name }}</h4>\n <div class=\"flex flex-col gap-2\">\n @for (definition of group.fields; track definition.type) {\n <button\n type=\"button\"\n class=\"ngs-form-builder-palette-item\"\n draggable=\"true\"\n (dragstart)=\"paletteDragStarted($event, definition)\"\n (dragend)=\"paletteDragEnded()\"\n (click)=\"paletteClicked(definition)\">\n <span class=\"ngs-form-builder-palette-item-content\">\n <span class=\"ngs-form-builder-palette-item-label\">\n <ngs-icon [name]=\"definition.icon || 'fluent:form-24-regular'\"/>\n <span>{{ definition.label }}</span>\n </span>\n </span>\n </button>\n }\n </div>\n </section>\n }\n </div>\n </ngs-scrollbar-area>\n </ngs-tab>\n <ngs-tab label=\"Layers\" class=\"relative\">\n <ngs-scrollbar-area>\n <div class=\"ps-1 pe-4 py-4\">\n <ngs-tree\n #actualFieldsTree=\"ngsTree\"\n [dataSource]=\"fieldTree()\"\n [childrenAccessor]=\"fieldTreeChildrenAccessor\"\n [trackBy]=\"trackFieldTreeNode\"\n [draggablePredicate]=\"fieldTreeDraggablePredicate\"\n [dropPredicate]=\"fieldTreeDropPredicate\"\n [nodePaddingIndent]=\"80\"\n [reorderOnDrop]=\"false\"\n (nodeDrop)=\"fieldTreeNodeDropped($event)\"\n draggable>\n <ng-template ngsTreeDragPlaceholder let-source=\"source\">\n <div class=\"ngs-form-builder-tree-drag-placeholder\">\n <ngs-icon [name]=\"fieldTreePlaceholderIcon(source)\"/>\n <span>{{ source.label }}</span>\n </div>\n </ng-template>\n\n <ngs-tree-node *ngsTreeNodeDef=\"let node\" ngsTreeNodePadding [value]=\"node.id\">\n <button\n ngsButton=\"text\"\n type=\"button\"\n class=\"ngs-form-builder-tree-item\"\n [class.is-selected]=\"selectedFieldId() === node.id\"\n [attr.data-form-builder-tree-node-id]=\"node.id\"\n (click)=\"selectFieldTreeNode(node)\">\n <ngs-icon [name]=\"node.icon\"/>\n {{ node.label }}\n<!-- <span class=\"inline-flex flex-col\">-->\n<!-- -->\n<!-- @if (node.name) {-->\n<!-- <small>{{ node.name }}</small>-->\n<!-- }-->\n<!-- </span>-->\n </button>\n </ngs-tree-node>\n\n <ngs-tree-node *ngsTreeNodeDef=\"let node; when: hasFieldTreeChildren\"\n ngsTreeNodePadding\n [value]=\"node.id\"\n [cdkTreeNodeTypeaheadLabel]=\"node.label\">\n <button\n ngsIconButton\n class=\"ngs-form-builder-tree-toggle\"\n [attr.aria-label]=\"'Toggle ' + node.label\"\n (click)=\"toggleFieldTreeNode(actualFieldsTree, node, $event)\">\n <ngs-icon [name]=\"isFieldTreeNodeExpanded(actualFieldsTree, node) ? 'fluent:chevron-down-24-regular' : 'fluent:chevron-right-24-regular'\" class=\"size-4\"/>\n </button>\n <button\n ngsButton=\"text\"\n type=\"button\"\n class=\"ngs-form-builder-tree-item\"\n [class.is-selected]=\"selectedFieldId() === node.id\"\n [attr.data-form-builder-tree-node-id]=\"node.id\"\n (click)=\"selectFieldTreeNode(node)\">\n <ngs-icon [name]=\"node.icon\"/>\n <span>{{ node.label }}</span>\n <!-- @if (node.name) {-->\n <!-- <small>{{ node.name }}</small>-->\n <!-- }-->\n </button>\n </ngs-tree-node>\n </ngs-tree>\n </div>\n </ngs-scrollbar-area>\n </ngs-tab>\n </ngs-tab-group>\n </ngs-panel-sidebar>\n\n <ngs-panel-content>\n <ngs-scrollbar-area [absolute]=\"true\">\n <div class=\"ngs-form-builder-canvas flex flex-col gap-4 p-5\">\n <div\n class=\"ngs-form-builder-canvas-list\"\n [id]=\"rootDropListId()\"\n (dragover)=\"nativeCanvasDragOver($event)\"\n (dragleave)=\"nativeDragLeave($event, rootDropListId())\"\n (drop)=\"nativeFieldDrop($event, rootDropListId())\">\n @for (item of canvasItems(); track item.kind + ':' + item.id; let itemIndex = $index) {\n @if (isNativeDropTarget(rootDropListId(), itemIndex)) {\n <ng-container [ngTemplateOutlet]=\"nativeDragSection() ? nativeSectionGhost : nativeFieldGhost\"/>\n }\n @if (item.field; as field) {\n @if (isContainerField(field)) {\n <div\n class=\"ngs-form-builder-field is-container\"\n [class.is-width-1]=\"(field.width ?? 12) === 1\"\n [class.is-width-2]=\"field.width === 2\"\n [class.is-width-3]=\"field.width === 3\"\n [class.is-width-4]=\"field.width === 4\"\n [class.is-width-5]=\"field.width === 5\"\n [class.is-width-6]=\"field.width === 6\"\n [class.is-width-7]=\"field.width === 7\"\n [class.is-width-8]=\"field.width === 8\"\n [class.is-width-9]=\"field.width === 9\"\n [class.is-width-10]=\"field.width === 10\"\n [class.is-width-11]=\"field.width === 11\"\n [class.is-width-12]=\"(field.width ?? 12) === 12\"\n [class.is-selected]=\"selectedFieldId() === field.id\"\n [attr.data-form-builder-field-id]=\"field.id\"\n (dragover)=\"nativeContainerFieldDragOver($event, field)\"\n (dragleave)=\"nativeContainerFieldDragLeave($event, field)\"\n (drop)=\"nativeContainerFieldDrop($event, field)\"\n (click)=\"$event.stopPropagation(); selectCanvasField(field)\">\n <div class=\"ngs-form-builder-field-header\">\n <div class=\"ngs-form-builder-field-label\">\n <ngs-icon [name]=\"fieldIcon(field)\"/>\n <span>{{ field.label }}</span>\n </div>\n </div>\n\n <div class=\"ngs-form-builder-grid-field\">\n <ng-container\n [ngTemplateOutlet]=\"fieldList\"\n [ngTemplateOutletContext]=\"{\n $implicit: field.children ?? [],\n containerId: fieldDropListId(field)\n }\"/>\n </div>\n </div>\n } @else {\n <div\n class=\"ngs-form-builder-field\"\n [class.is-width-1]=\"(field.width ?? 12) === 1\"\n [class.is-width-2]=\"field.width === 2\"\n [class.is-width-3]=\"field.width === 3\"\n [class.is-width-4]=\"field.width === 4\"\n [class.is-width-5]=\"field.width === 5\"\n [class.is-width-6]=\"field.width === 6\"\n [class.is-width-7]=\"field.width === 7\"\n [class.is-width-8]=\"field.width === 8\"\n [class.is-width-9]=\"field.width === 9\"\n [class.is-width-10]=\"field.width === 10\"\n [class.is-width-11]=\"field.width === 11\"\n [class.is-width-12]=\"(field.width ?? 12) === 12\"\n [class.is-selected]=\"selectedFieldId() === field.id\"\n [attr.data-form-builder-field-id]=\"field.id\"\n (click)=\"$event.stopPropagation(); selectCanvasField(field)\">\n <div class=\"ngs-form-builder-field-header\">\n <div class=\"ngs-form-builder-field-label\">\n <ngs-icon [name]=\"fieldIcon(field)\"/>\n <span>{{ field.label }}</span>\n </div>\n </div>\n\n <ngs-form-builder-field-host\n [field]=\"field\"\n [control]=\"previewControl(field)\"\n [definitions]=\"definitions()\"\n [uploadCallback]=\"uploadCallback()\"\n [readonly]=\"true\"\n [editableCanvas]=\"true\"/>\n </div>\n }\n } @else if (item.section; as section) {\n <ngs-card class=\"ngs-form-builder-section\"\n [attr.data-form-builder-section-id]=\"section.id\"\n (dragover)=\"nativeSectionDragOver($event, section)\"\n (dragleave)=\"nativeDragLeave($event, sectionDropListId(section))\"\n (drop)=\"nativeSectionDrop($event, section)\"\n (click)=\"selectSection(section)\">\n <ngs-card-header>\n <div class=\"ngs-form-builder-section-heading\">\n <input ngsInput\n class=\"ngs-form-builder-section-title\"\n [ngModel]=\"section.title\"\n [ngModelOptions]=\"{standalone: true}\"\n (ngModelChange)=\"updateSection(section, { title: $event })\"\n aria-label=\"Section title\">\n </div>\n @if (section.description) {\n <p class=\"truncate text-sm text-secondary\">{{ section.description }}</p>\n }\n\n <ngs-card-aside>\n <div class=\"ngs-form-builder-section-actions\">\n <button ngsIconButton type=\"button\" aria-label=\"Collapse section\" (click)=\"updateSection(section, { collapsed: !section.collapsed })\">\n <ngs-icon [name]=\"isSectionCollapsed(section) ? 'fluent:chevron-down-24-regular' : 'fluent:chevron-up-24-regular'\"/>\n </button>\n <button ngsIconButton type=\"button\" aria-label=\"Delete section\" (click)=\"removeSection(section)\">\n <ngs-icon name=\"fluent:delete-24-regular\"/>\n </button>\n </div>\n </ngs-card-aside>\n </ngs-card-header>\n\n @if (!isSectionCollapsed(section)) {\n <ngs-card-content>\n <ng-container\n [ngTemplateOutlet]=\"fieldList\"\n [ngTemplateOutletContext]=\"{\n $implicit: section.fields,\n containerId: sectionDropListId(section),\n section\n }\"/>\n </ngs-card-content>\n } @else if (isNativeDropTarget(sectionDropListId(section), section.fields.length)) {\n <ngs-card-content>\n <div class=\"ngs-form-builder-drop-list ngs-form-builder-canvas-drop-list\">\n <ng-container [ngTemplateOutlet]=\"nativeFieldGhost\"/>\n </div>\n </ngs-card-content>\n }\n </ngs-card>\n }\n }\n\n @if (isNativeDropTarget(rootDropListId(), canvasItems().length)) {\n <ng-container [ngTemplateOutlet]=\"nativeDragSection() ? nativeSectionGhost : nativeFieldGhost\"/>\n }\n\n </div>\n </div>\n </ngs-scrollbar-area>\n </ngs-panel-content>\n\n <ngs-panel-aside class=\"ngs-form-builder-inspector w-88 border-s border-s-subtle\">\n <ngs-scrollbar-area [absolute]=\"true\">\n <div class=\"flex flex-col gap-4 p-5\">\n <div class=\"flex items-start justify-between gap-3\">\n <div class=\"min-w-0\">\n <h3 class=\"text-base font-semibold\">{{ inspectorTitle() }}</h3>\n @if (selectedField() || selectedSection()) {\n <p class=\"truncate text-sm text-secondary\">{{ selectedField()?.label || selectedSection()?.title }}</p>\n }\n </div>\n <button ngsIconButton type=\"button\" aria-label=\"Clear selection\" (click)=\"selectedFieldId.set(null)\">\n <ngs-icon name=\"fluent:dismiss-24-regular\"/>\n </button>\n </div>\n\n @if (selectedField() || selectedSection()) {\n <ngs-form-builder-settings-host\n [field]=\"selectedField()\"\n [section]=\"selectedSection()\"\n [schema]=\"schema()\"\n [definitions]=\"definitions()\"\n [settingsDefinitions]=\"settingsDefinitions()\"\n [update]=\"updateSelectedField\"\n [updateSection]=\"updateSelectedSection\"/>\n\n @if (selectedField(); as field) {\n <div class=\"mt-3\">\n <button ngsButton=\"text\" type=\"button\" class=\"ngs-form-builder-delete-button\" (click)=\"confirmRemoveField(field)\">\n <ngs-icon name=\"fluent:delete-24-regular\"/>\n Delete field\n </button>\n </div>\n }\n } @else {\n <div class=\"ngs-form-builder-empty-inspector\">\n Select a field on the canvas or in the fields tree to edit its settings.\n </div>\n }\n </div>\n </ngs-scrollbar-area>\n </ngs-panel-aside>\n</ngs-panel>\n\n<ng-template #previewDialog>\n <h3 ngs-dialog-title>{{ schema().title || 'Form preview' }}</h3>\n <ngs-dialog-content class=\"ngs-form-builder-preview-dialog-content\">\n <ngs-form-renderer\n [schema]=\"schema()\"\n [uploadCallback]=\"uploadCallback()\"\n [showSubmit]=\"false\"/>\n </ngs-dialog-content>\n <ngs-dialog-actions align=\"end\">\n <button ngsButton=\"outlined\" type=\"button\" ngs-dialog-close>Close</button>\n </ngs-dialog-actions>\n</ng-template>\n", styles: [":host{display:block;min-height:640px}:host .ngs-form-builder-shell{height:100%;min-height:640px;overflow:hidden}:host .ngs-form-builder-palette-item{display:flex;width:100%;min-height:calc(var(--spacing, .25rem) * 12);align-items:center;border:1px solid var(--ngs-color-outline-variant);border-radius:var(--ngs-radius-lg);background:var(--ngs-color-surface);color:var(--ngs-color-on-surface);cursor:grab;font-size:var(--ngs-font-size-sm);font-weight:500;padding:0 calc(var(--spacing, .25rem) * 3);text-align:start;transition:background-color .16s cubic-bezier(0,0,.2,1),border-color .16s cubic-bezier(0,0,.2,1),box-shadow .16s cubic-bezier(0,0,.2,1)}:host .ngs-form-builder-palette-item:hover{border-color:var(--ngs-color-primary);background:var(--ngs-color-primary);color:var(--ngs-color-primary)}@supports (color: color-mix(in lab,red,red)){:host .ngs-form-builder-palette-item:hover{background:color-mix(in srgb,var(--ngs-color-primary),var(--ngs-color-surface) 96%)}}:host .ngs-form-builder-palette-item:focus-visible{outline:0;box-shadow:0 0 0 3px var(--ngs-state-focus-ring)}:host .ngs-form-builder-palette-item:active{cursor:grabbing}:host .ngs-form-builder-palette-item-preview{width:calc(var(--spacing, .25rem) * 64)}:host .ngs-form-builder-native-drag-image{position:fixed;inset-block-start:0;inset-inline-start:0;width:calc(var(--spacing, .25rem) * 64);pointer-events:none;transform:translate(-200vw,-200vh);z-index:-1}:host .ngs-form-builder-palette-item-placeholder{width:100%;box-shadow:none;opacity:1;pointer-events:none}:host .ngs-form-builder-palette{background:var(--ngs-color-surface-container-low)}:host .ngs-form-builder-palette-item-content{display:inline-flex;min-width:0;width:100%;align-items:center;justify-content:space-between;gap:calc(var(--spacing, .25rem) * 3)}:host .ngs-form-builder-palette-item-label{display:inline-flex;min-width:0;align-items:center;gap:calc(var(--spacing, .25rem) * 2)}:host .ngs-form-builder-palette-item-label span{min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}:host ngs-tab-group{--ngs-tab-label-height: calc(var(--spacing, .25rem) * 10)}:host ngs-tree{--ngs-tree-node-gap: calc(var(--spacing, .25rem) * 1)}:host ngs-tree ngs-tree-node{cursor:grab}:host ngs-tree ngs-tree-node:active{cursor:grabbing}:host .ngs-form-builder-tree-item{min-width:0;flex:1;justify-content:flex-start;overflow:hidden}:host .ngs-form-builder-tree-item span{min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}:host .ngs-form-builder-tree-item small{margin-inline-start:auto;color:var(--ngs-color-on-surface-variant);font-size:var(--ngs-font-size-xs);font-weight:400}:host .ngs-form-builder-tree-item.is-selected{background:var(--ngs-color-primary-container);color:var(--ngs-color-on-primary-container)}:host .ngs-form-builder-tree-item.is-selected small{color:currentColor}:host .ngs-form-builder-tree-drag-placeholder{display:flex;min-width:0;width:100%;min-height:calc(var(--spacing, .25rem) * 10);align-items:center;gap:calc(var(--spacing, .25rem) * 2);border:1px dashed var(--ngs-color-primary);border-radius:var(--ngs-radius-md);background:var(--ngs-color-primary);color:var(--ngs-color-primary);font-size:var(--ngs-font-size-sm);font-weight:500;padding:calc(var(--spacing, .25rem) * 2) calc(var(--spacing, .25rem) * 3);pointer-events:none}@supports (color: color-mix(in lab,red,red)){:host .ngs-form-builder-tree-drag-placeholder{background:color-mix(in srgb,var(--ngs-color-primary),var(--ngs-color-surface) 94%)}}:host .ngs-form-builder-tree-drag-placeholder ngs-icon{flex:none}:host .ngs-form-builder-tree-drag-placeholder span{min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}:host .ngs-form-builder-drag-icon{display:inline-flex;flex:none;align-items:center;color:var(--ngs-color-on-surface-variant)}:host .ngs-form-builder-canvas{min-height:100%}:host .ngs-form-builder-section{--ngs-card-padding: calc(var(--spacing, .25rem) * 4);grid-column:span 12}:host .ngs-form-builder-native-ghost-section{border-color:var(--ngs-color-primary);background:var(--ngs-color-primary);box-shadow:none;opacity:1;pointer-events:none}@supports (color: color-mix(in lab,red,red)){:host .ngs-form-builder-native-ghost-section{background:color-mix(in srgb,var(--ngs-color-primary),var(--ngs-color-surface) 94%)}}:host .ngs-form-builder-canvas-list{display:grid;grid-template-columns:repeat(12,minmax(0,1fr));gap:calc(var(--spacing, .25rem) * 4)}:host .ngs-form-builder-section-heading{display:flex;min-width:0;flex:1;align-items:center;gap:calc(var(--spacing, .25rem) * 3)}:host .ngs-form-builder-section-actions{display:flex;align-items:center;gap:calc(var(--spacing, .25rem) * 1)}:host .ngs-form-builder-section-title{width:100%;min-width:0;height:auto;min-height:0;padding:0;border:0;background:transparent;color:var(--ngs-color-on-surface);font-size:var(--ngs-font-size-base);font-weight:600;line-height:var(--ngs-line-height-base);outline:0}:host .ngs-form-builder-section-title:focus{outline:2px solid var(--ngs-color-primary);outline-offset:2px;border-radius:var(--ngs-radius-sm)}:host .ngs-form-builder-section-preview-title{min-width:0;overflow:hidden;color:var(--ngs-color-on-surface);font-size:var(--ngs-font-size-base);font-weight:600;line-height:var(--ngs-line-height-base);text-overflow:ellipsis;white-space:nowrap}:host .ngs-form-builder-drop-list{display:grid;grid-template-columns:repeat(12,minmax(0,1fr));gap:calc(var(--spacing, .25rem) * 4);min-height:calc(var(--spacing, .25rem) * 16)}:host .ngs-form-builder-field{position:relative;display:flex;flex-direction:column;grid-column:span 12;gap:calc(var(--spacing, .25rem) * 3);border:1px solid transparent;border-radius:var(--ngs-radius-lg);padding:calc(var(--spacing, .25rem) * 3);background:var(--ngs-color-surface);cursor:pointer}:host .ngs-form-builder-field.is-container{align-items:stretch;background:var(--ngs-color-surface-container-lowest)}:host .ngs-form-builder-field.is-width-1{grid-column:span 1}:host .ngs-form-builder-field.is-width-2{grid-column:span 2}:host .ngs-form-builder-field.is-width-3{grid-column:span 3}:host .ngs-form-builder-field.is-width-4{grid-column:span 4}:host .ngs-form-builder-field.is-width-5{grid-column:span 5}:host .ngs-form-builder-field.is-width-6{grid-column:span 6}:host .ngs-form-builder-field.is-width-7{grid-column:span 7}:host .ngs-form-builder-field.is-width-8{grid-column:span 8}:host .ngs-form-builder-field.is-width-9{grid-column:span 9}:host .ngs-form-builder-field.is-width-10{grid-column:span 10}:host .ngs-form-builder-field.is-width-11{grid-column:span 11}:host .ngs-form-builder-field.is-width-12{grid-column:span 12}:host .ngs-form-builder-field:hover{border-color:var(--ngs-color-outline-variant)}:host .ngs-form-builder-field.is-selected{border-color:var(--ngs-color-primary);box-shadow:0 0 0 1px var(--ngs-color-primary)}:host .ngs-form-builder-field-header{display:flex;min-width:0;align-items:center;justify-content:space-between;gap:calc(var(--spacing, .25rem) * 3)}:host .ngs-form-builder-field-label{display:flex;min-width:0;align-items:center;gap:calc(var(--spacing, .25rem) * 2);color:var(--ngs-color-on-surface);font-size:var(--ngs-font-size-sm);font-weight:600}:host .ngs-form-builder-field-label ngs-icon{flex:none;color:var(--ngs-color-primary)}:host .ngs-form-builder-field-label span{min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}:host .ngs-form-builder-field-host{min-width:0;width:100%;grid-column:auto}:host .ngs-form-builder-grid-field{display:flex;min-width:0;flex-direction:column;gap:calc(var(--spacing, .25rem) * 3)}:host .ngs-form-builder-grid-field-header{display:flex;align-items:center;justify-content:space-between;gap:calc(var(--spacing, .25rem) * 3);border:1px solid var(--ngs-color-outline-variant);border-radius:var(--ngs-radius-md);background:var(--ngs-color-surface);padding:calc(var(--spacing, .25rem) * 3)}:host .ngs-form-builder-grid-field-header p{overflow:hidden;color:var(--ngs-color-on-surface);font-size:var(--ngs-font-size-sm);font-weight:600;text-overflow:ellipsis;white-space:nowrap}:host .ngs-form-builder-grid-field-header span{display:block;overflow:hidden;color:var(--ngs-color-on-surface-variant);font-size:var(--ngs-font-size-xs);text-overflow:ellipsis;white-space:nowrap}:host .ngs-form-builder-empty-inspector{display:flex;min-height:calc(var(--spacing, .25rem) * 14);align-items:center;justify-content:center;border:1px dashed var(--ngs-color-primary);border-radius:var(--ngs-radius-lg);color:var(--ngs-color-on-surface-variant);font-size:var(--ngs-font-size-sm);grid-column:1/-1;padding:calc(var(--spacing, .25rem) * 4);text-align:center}:host .ngs-form-builder-empty-inspector{min-height:calc(var(--spacing, .25rem) * 28);border-color:var(--ngs-color-outline-variant)}:host .ngs-form-builder-delete-button{justify-content:flex-start;color:var(--ngs-color-danger)}:host .ngs-form-builder-preview-dialog-content{width:min(100%,760px)}:host .ngs-form-builder-field-placeholder{border-style:dashed;border-color:var(--ngs-color-primary);background:var(--ngs-color-primary);box-shadow:none;opacity:1;pointer-events:none}@supports (color: color-mix(in lab,red,red)){:host .ngs-form-builder-field-placeholder{background:color-mix(in srgb,var(--ngs-color-primary),var(--ngs-color-surface) 92%)}}:host .ngs-form-builder-ghost-control{height:calc(var(--spacing, .25rem) * 10);border:1px solid var(--ngs-color-outline-variant);border-radius:var(--ngs-radius-md);background:var(--ngs-color-surface)}\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: 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: 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: Card, selector: "ngs-card", inputs: ["appearance"], exportAs: ["ngsCard"] }, { kind: "component", type: CardAside, selector: "ngs-card-aside, [ngs-card-aside], ngsCardAside" }, { kind: "component", type: CardContent, selector: "ngs-card-content, [ngs-card-content], [ngsCardContent]", inputs: ["withoutPadding"], exportAs: ["ngsCardContent"] }, { kind: "component", type: CardHeader, selector: "ngs-card-header", exportAs: ["ngsCardHeader"] }, { kind: "component", type: DialogActions, selector: "ngs-dialog-actions, [ngs-dialog-actions], [ngsDialogActions]", inputs: ["align"] }, { kind: "directive", type: DialogClose, selector: "[ngs-dialog-close], [ngsDialogClose]", inputs: ["ngs-dialog-close", "ngsDialogClose", "ariaLabel", "type"], exportAs: ["ngsDialogClose"] }, { kind: "component", type: DialogContent, selector: "ngs-dialog-content,[ngs-dialog-content],[ngsDialogContent]" }, { kind: "component", type: DialogTitle, selector: "ngs-dialog-title, [ngs-dialog-title], [ngsDialogTitle]", inputs: ["id"], exportAs: ["ngsDialogTitle"] }, { kind: "component", type: Icon, selector: "ngs-icon", inputs: ["name"], exportAs: ["ngsIcon"] }, { kind: "directive", type: Input, selector: "input[ngsInput], textarea[ngsInput]", inputs: ["id", "placeholder", "required", "disabled", "readonly", "errorStateMatcher"], exportAs: ["ngsInput"] }, { kind: "component", type: Panel, selector: "ngs-panel", inputs: ["absolute"], exportAs: ["ngsPanel"] }, { kind: "component", type: PanelAside, selector: "ngs-panel-aside" }, { kind: "component", type: PanelContent, selector: "ngs-panel-content", exportAs: ["ngsPanelContent"] }, { kind: "component", type: PanelHeader, selector: "ngs-panel-header", inputs: ["flex", "autoHeight"], exportAs: ["ngsPanelHeader"] }, { kind: "component", type: PanelSidebar, selector: "ngs-panel-sidebar" }, { kind: "component", type: ScrollbarArea, selector: "ngs-scrollbar-area", inputs: ["scrollbarWidth", "autoHide", "absolute"], outputs: ["scrolled"], exportAs: ["ngsScrollbarArea"] }, { kind: "component", type: Tab, selector: "ngs-tab", inputs: ["label", "aria-label", "aria-labelledby", "disabled"], exportAs: ["ngsTab"] }, { kind: "component", type: TabGroup, selector: "ngs-tab-group", inputs: ["selectedIndex", "headerPosition", "preserveContent", "ngs-stretch-tabs", "ngs-align-tabs", "disableRipple", "animationDuration", "animate.enter", "animate.leave"], outputs: ["selectedIndexChange", "selectedTabChange", "focusChange"] }, { kind: "component", type: Tree, selector: "ngs-tree", inputs: ["checkable", "selectable", "draggable", "draggablePredicate", "dropPredicate", "reorderOnDrop", "dragPreview", "nodePaddingIndent", "childrenKey", "filterValue", "filterPredicate", "filterMode"], outputs: ["checkedChange", "selectedChange", "nodeDrop"], exportAs: ["ngsTree"] }, { kind: "directive", type: TreeDragPlaceholder, selector: "ng-template[ngsTreeDragPlaceholder]" }, { kind: "component", type: TreeNode, selector: "ngs-tree-node", inputs: ["tabIndex", "value", "disabled"], outputs: ["activation", "expandedChange"], exportAs: ["ngsTreeNode"] }, { kind: "directive", type: TreeNodeDef, selector: "[ngsTreeNodeDef]", inputs: ["ngsTreeNodeDefWhen", "ngsTreeNode"] }, { kind: "directive", type: TreeNodePadding, selector: "[ngsTreeNodePadding]", inputs: ["ngsTreeNodePadding", "ngsTreeNodePaddingIndent"] }, { kind: "component", type: FormBuilderFieldHost, selector: "ngs-form-builder-field-host", inputs: ["field", "control", "definitions", "readonly", "editableCanvas", "uploadCallback"], exportAs: ["ngsFormBuilderFieldHost"] }, { kind: "component", type: FormBuilderRenderer, selector: "ngs-form-renderer", inputs: ["schema", "readonly", "showSubmit", "submitLabel", "uploadCallback", "value"], outputs: ["valueChange", "formSubmit", "formReady"], exportAs: ["ngsFormRenderer"] }, { kind: "component", type: FormBuilderSettingsHost, selector: "ngs-form-builder-settings-host", inputs: ["field", "section", "schema", "definitions", "settingsDefinitions", "update", "updateSection"], exportAs: ["ngsFormBuilderSettingsHost"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
1772
2170
  }
1773
2171
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: FormBuilder, decorators: [{
1774
2172
  type: Component,
@@ -1804,8 +2202,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.4", ngImpor
1804
2202
  FormBuilderSettingsHost
1805
2203
  ], changeDetection: ChangeDetectionStrategy.OnPush, host: {
1806
2204
  'class': 'ngs-form-builder'
1807
- }, template: "<ng-template #nativeFieldGhost>\n @if (nativeDragFieldDefinition(); as definition) {\n <div\n class=\"ngs-form-builder-field ngs-form-builder-field-placeholder ngs-form-builder-native-ghost-field\"\n [class.is-width-1]=\"definitionWidth(definition) === 1\"\n [class.is-width-2]=\"definitionWidth(definition) === 2\"\n [class.is-width-3]=\"definitionWidth(definition) === 3\"\n [class.is-width-4]=\"definitionWidth(definition) === 4\"\n [class.is-width-5]=\"definitionWidth(definition) === 5\"\n [class.is-width-6]=\"definitionWidth(definition) === 6\"\n [class.is-width-7]=\"definitionWidth(definition) === 7\"\n [class.is-width-8]=\"definitionWidth(definition) === 8\"\n [class.is-width-9]=\"definitionWidth(definition) === 9\"\n [class.is-width-10]=\"definitionWidth(definition) === 10\"\n [class.is-width-11]=\"definitionWidth(definition) === 11\"\n [class.is-width-12]=\"definitionWidth(definition) === 12\">\n <div class=\"ngs-form-builder-field-header\">\n <div class=\"ngs-form-builder-field-label\">\n <ngs-icon [name]=\"definition.icon || 'fluent:form-24-regular'\"/>\n <span>{{ definition.defaults?.label || definition.label }}</span>\n </div>\n <span class=\"ngs-form-builder-drag-icon\">\n <ngs-icon name=\"fluent:arrow-move-24-regular\"/>\n </span>\n </div>\n <div class=\"ngs-form-builder-ghost-control\"></div>\n </div>\n }\n</ng-template>\n\n<ng-template #nativeSectionGhost>\n <ngs-card class=\"ngs-form-builder-section ngs-form-builder-native-ghost-section\">\n <ngs-card-header>\n <div class=\"ngs-form-builder-section-heading\">\n <span class=\"ngs-form-builder-drag-icon\">\n <ngs-icon name=\"fluent:folder-add-24-regular\"/>\n </span>\n <span class=\"ngs-form-builder-section-title\">Section</span>\n </div>\n </ngs-card-header>\n </ngs-card>\n</ng-template>\n\n<ng-template #fieldList let-fields let-containerId=\"containerId\" let-section=\"section\">\n <div\n class=\"ngs-form-builder-drop-list ngs-form-builder-canvas-drop-list\"\n [id]=\"containerId\"\n (dragover)=\"nativeFieldDragOver($event, containerId)\"\n (dragleave)=\"nativeDragLeave($event, containerId)\"\n (drop)=\"nativeFieldDrop($event, containerId)\">\n @for (field of fields; track field.id; let fieldIndex = $index) {\n @if (isNativeDropTarget(containerId, fieldIndex)) {\n <ng-container [ngTemplateOutlet]=\"nativeFieldGhost\"/>\n }\n @if (isContainerField(field)) {\n <div\n class=\"ngs-form-builder-field is-container\"\n [class.is-width-1]=\"(field.width ?? 12) === 1\"\n [class.is-width-2]=\"field.width === 2\"\n [class.is-width-3]=\"field.width === 3\"\n [class.is-width-4]=\"field.width === 4\"\n [class.is-width-5]=\"field.width === 5\"\n [class.is-width-6]=\"field.width === 6\"\n [class.is-width-7]=\"field.width === 7\"\n [class.is-width-8]=\"field.width === 8\"\n [class.is-width-9]=\"field.width === 9\"\n [class.is-width-10]=\"field.width === 10\"\n [class.is-width-11]=\"field.width === 11\"\n [class.is-width-12]=\"(field.width ?? 12) === 12\"\n [class.is-selected]=\"selectedFieldId() === field.id\"\n [attr.data-form-builder-field-id]=\"field.id\"\n (dragover)=\"nativeContainerFieldDragOver($event, field)\"\n (dragleave)=\"nativeContainerFieldDragLeave($event, field)\"\n (drop)=\"nativeContainerFieldDrop($event, field)\"\n (click)=\"$event.stopPropagation(); selectCanvasField(field, section)\">\n <div class=\"ngs-form-builder-field-header\">\n <div class=\"ngs-form-builder-field-label\">\n <ngs-icon [name]=\"fieldIcon(field)\"/>\n <span>{{ field.label }}</span>\n </div>\n </div>\n\n <div class=\"ngs-form-builder-grid-field\">\n <ng-container\n [ngTemplateOutlet]=\"fieldList\"\n [ngTemplateOutletContext]=\"{\n $implicit: field.children ?? [],\n containerId: fieldDropListId(field),\n section\n }\"/>\n </div>\n </div>\n } @else {\n <div\n class=\"ngs-form-builder-field\"\n [class.is-width-1]=\"(field.width ?? 12) === 1\"\n [class.is-width-2]=\"field.width === 2\"\n [class.is-width-3]=\"field.width === 3\"\n [class.is-width-4]=\"field.width === 4\"\n [class.is-width-5]=\"field.width === 5\"\n [class.is-width-6]=\"field.width === 6\"\n [class.is-width-7]=\"field.width === 7\"\n [class.is-width-8]=\"field.width === 8\"\n [class.is-width-9]=\"field.width === 9\"\n [class.is-width-10]=\"field.width === 10\"\n [class.is-width-11]=\"field.width === 11\"\n [class.is-width-12]=\"(field.width ?? 12) === 12\"\n [class.is-selected]=\"selectedFieldId() === field.id\"\n [attr.data-form-builder-field-id]=\"field.id\"\n (click)=\"$event.stopPropagation(); selectCanvasField(field, section)\">\n <div class=\"ngs-form-builder-field-header\">\n <div class=\"ngs-form-builder-field-label\">\n <ngs-icon [name]=\"fieldIcon(field)\"/>\n <span>{{ field.label }}</span>\n </div>\n </div>\n\n <ngs-form-builder-field-host\n [field]=\"field\"\n [control]=\"previewControl(field)\"\n [definitions]=\"definitions()\"\n [readonly]=\"true\"\n [editableCanvas]=\"true\"/>\n </div>\n }\n }\n\n @if (isNativeDropTarget(containerId, fields.length)) {\n <ng-container [ngTemplateOutlet]=\"nativeFieldGhost\"/>\n }\n </div>\n</ng-template>\n\n<ngs-panel class=\"ngs-form-builder-shell\">\n <ngs-panel-header autoHeight class=\"flex items-center justify-between gap-4 border-b border-b-subtle px-5 py-3\">\n <div class=\"min-w-0\">\n <p class=\"text-sm text-secondary\">Form builder</p>\n <h2 class=\"truncate text-lg font-semibold\">{{ schema().title || 'Untitled form' }}</h2>\n </div>\n\n <div class=\"flex items-center gap-2\">\n <button ngsButton=\"outlined\" type=\"button\" (click)=\"openPreview(previewDialog)\">\n <ngs-icon name=\"fluent:eye-24-regular\"/>\n Preview\n </button>\n </div>\n </ngs-panel-header>\n\n <ngs-panel-sidebar class=\"ngs-form-builder-palette w-80 border-e border-e-subtle\">\n <ngs-tab-group\n animationDuration=\"0ms\"\n [selectedIndex]=\"fieldsTabIndex()\"\n (selectedIndexChange)=\"fieldsTabIndex.set($event)\">\n <ngs-tab label=\"Placeholders\" class=\"relative\">\n <ngs-scrollbar-area>\n <div class=\"flex flex-col gap-5 p-4\">\n <section class=\"flex flex-col gap-2\">\n <h4 class=\"text-xs font-semibold uppercase tracking-normal text-secondary\">Layout</h4>\n <button\n type=\"button\"\n class=\"ngs-form-builder-palette-item\"\n draggable=\"true\"\n (dragstart)=\"sectionPaletteDragStarted($event)\"\n (dragend)=\"paletteDragEnded()\">\n <span class=\"ngs-form-builder-palette-item-content\">\n <span class=\"ngs-form-builder-palette-item-label\">\n <ngs-icon name=\"fluent:folder-add-24-regular\"/>\n <span>Section</span>\n </span>\n </span>\n </button>\n\n @for (definition of layoutDefinitions(); track definition.type) {\n <button\n type=\"button\"\n class=\"ngs-form-builder-palette-item\"\n draggable=\"true\"\n (dragstart)=\"paletteDragStarted($event, definition)\"\n (dragend)=\"paletteDragEnded()\"\n (click)=\"paletteClicked(definition)\">\n <span class=\"ngs-form-builder-palette-item-content\">\n <span class=\"ngs-form-builder-palette-item-label\">\n <ngs-icon [name]=\"definition.icon || 'fluent:form-24-regular'\"/>\n <span>{{ definition.label }}</span>\n </span>\n </span>\n </button>\n }\n </section>\n\n @for (group of paletteGroups(); track group.name) {\n <section class=\"flex flex-col gap-2\">\n <h4 class=\"text-xs font-semibold uppercase tracking-normal text-secondary\">{{ group.name }}</h4>\n <div class=\"flex flex-col gap-2\">\n @for (definition of group.fields; track definition.type) {\n <button\n type=\"button\"\n class=\"ngs-form-builder-palette-item\"\n draggable=\"true\"\n (dragstart)=\"paletteDragStarted($event, definition)\"\n (dragend)=\"paletteDragEnded()\"\n (click)=\"paletteClicked(definition)\">\n <span class=\"ngs-form-builder-palette-item-content\">\n <span class=\"ngs-form-builder-palette-item-label\">\n <ngs-icon [name]=\"definition.icon || 'fluent:form-24-regular'\"/>\n <span>{{ definition.label }}</span>\n </span>\n </span>\n </button>\n }\n </div>\n </section>\n }\n </div>\n </ngs-scrollbar-area>\n </ngs-tab>\n <ngs-tab label=\"Actual fields\" class=\"relative\">\n <ngs-scrollbar-area>\n <div class=\"ps-1 pe-4 py-4\">\n <ngs-tree\n #actualFieldsTree=\"ngsTree\"\n [dataSource]=\"fieldTree()\"\n [childrenAccessor]=\"fieldTreeChildrenAccessor\"\n [trackBy]=\"trackFieldTreeNode\"\n [draggablePredicate]=\"fieldTreeDraggablePredicate\"\n [dropPredicate]=\"fieldTreeDropPredicate\"\n [nodePaddingIndent]=\"80\"\n [reorderOnDrop]=\"false\"\n (nodeDrop)=\"fieldTreeNodeDropped($event)\"\n draggable>\n <ng-template ngsTreeDragPlaceholder let-source=\"source\">\n <div class=\"ngs-form-builder-tree-drag-placeholder\">\n <ngs-icon [name]=\"fieldTreePlaceholderIcon(source)\"/>\n <span>{{ source.label }}</span>\n </div>\n </ng-template>\n\n <ngs-tree-node *ngsTreeNodeDef=\"let node\" ngsTreeNodePadding [value]=\"node.id\">\n <button\n ngsButton=\"text\"\n type=\"button\"\n class=\"ngs-form-builder-tree-item\"\n [class.is-selected]=\"selectedFieldId() === node.id\"\n [attr.data-form-builder-tree-node-id]=\"node.id\"\n (click)=\"selectFieldTreeNode(node)\">\n <ngs-icon [name]=\"node.icon\"/>\n {{ node.label }}\n<!-- <span class=\"inline-flex flex-col\">-->\n<!-- -->\n<!-- @if (node.name) {-->\n<!-- <small>{{ node.name }}</small>-->\n<!-- }-->\n<!-- </span>-->\n </button>\n </ngs-tree-node>\n\n <ngs-tree-node *ngsTreeNodeDef=\"let node; when: hasFieldTreeChildren\"\n ngsTreeNodePadding\n [value]=\"node.id\"\n [cdkTreeNodeTypeaheadLabel]=\"node.label\">\n <button\n ngsIconButton\n class=\"ngs-form-builder-tree-toggle\"\n [attr.aria-label]=\"'Toggle ' + node.label\"\n (click)=\"toggleFieldTreeNode(actualFieldsTree, node, $event)\">\n <ngs-icon [name]=\"isFieldTreeNodeExpanded(actualFieldsTree, node) ? 'fluent:chevron-down-24-regular' : 'fluent:chevron-right-24-regular'\" class=\"size-4\"/>\n </button>\n <button\n ngsButton=\"text\"\n type=\"button\"\n class=\"ngs-form-builder-tree-item\"\n [class.is-selected]=\"selectedFieldId() === node.id\"\n [attr.data-form-builder-tree-node-id]=\"node.id\"\n (click)=\"selectFieldTreeNode(node)\">\n <ngs-icon [name]=\"node.icon\"/>\n <span>{{ node.label }}</span>\n <!-- @if (node.name) {-->\n <!-- <small>{{ node.name }}</small>-->\n <!-- }-->\n </button>\n </ngs-tree-node>\n </ngs-tree>\n </div>\n </ngs-scrollbar-area>\n </ngs-tab>\n </ngs-tab-group>\n </ngs-panel-sidebar>\n\n <ngs-panel-content>\n <ngs-scrollbar-area [absolute]=\"true\">\n <div class=\"ngs-form-builder-canvas flex flex-col gap-4 p-5\">\n <div\n class=\"ngs-form-builder-canvas-list\"\n [id]=\"rootDropListId()\"\n (dragover)=\"nativeCanvasDragOver($event)\"\n (dragleave)=\"nativeDragLeave($event, rootDropListId())\"\n (drop)=\"nativeFieldDrop($event, rootDropListId())\">\n @for (item of canvasItems(); track item.kind + ':' + item.id; let itemIndex = $index) {\n @if (isNativeDropTarget(rootDropListId(), itemIndex)) {\n <ng-container [ngTemplateOutlet]=\"nativeDragSection() ? nativeSectionGhost : nativeFieldGhost\"/>\n }\n @if (item.field; as field) {\n @if (isContainerField(field)) {\n <div\n class=\"ngs-form-builder-field is-container\"\n [class.is-width-1]=\"(field.width ?? 12) === 1\"\n [class.is-width-2]=\"field.width === 2\"\n [class.is-width-3]=\"field.width === 3\"\n [class.is-width-4]=\"field.width === 4\"\n [class.is-width-5]=\"field.width === 5\"\n [class.is-width-6]=\"field.width === 6\"\n [class.is-width-7]=\"field.width === 7\"\n [class.is-width-8]=\"field.width === 8\"\n [class.is-width-9]=\"field.width === 9\"\n [class.is-width-10]=\"field.width === 10\"\n [class.is-width-11]=\"field.width === 11\"\n [class.is-width-12]=\"(field.width ?? 12) === 12\"\n [class.is-selected]=\"selectedFieldId() === field.id\"\n [attr.data-form-builder-field-id]=\"field.id\"\n (dragover)=\"nativeContainerFieldDragOver($event, field)\"\n (dragleave)=\"nativeContainerFieldDragLeave($event, field)\"\n (drop)=\"nativeContainerFieldDrop($event, field)\"\n (click)=\"$event.stopPropagation(); selectCanvasField(field)\">\n <div class=\"ngs-form-builder-field-header\">\n <div class=\"ngs-form-builder-field-label\">\n <ngs-icon [name]=\"fieldIcon(field)\"/>\n <span>{{ field.label }}</span>\n </div>\n </div>\n\n <div class=\"ngs-form-builder-grid-field\">\n <ng-container\n [ngTemplateOutlet]=\"fieldList\"\n [ngTemplateOutletContext]=\"{\n $implicit: field.children ?? [],\n containerId: fieldDropListId(field)\n }\"/>\n </div>\n </div>\n } @else {\n <div\n class=\"ngs-form-builder-field\"\n [class.is-width-1]=\"(field.width ?? 12) === 1\"\n [class.is-width-2]=\"field.width === 2\"\n [class.is-width-3]=\"field.width === 3\"\n [class.is-width-4]=\"field.width === 4\"\n [class.is-width-5]=\"field.width === 5\"\n [class.is-width-6]=\"field.width === 6\"\n [class.is-width-7]=\"field.width === 7\"\n [class.is-width-8]=\"field.width === 8\"\n [class.is-width-9]=\"field.width === 9\"\n [class.is-width-10]=\"field.width === 10\"\n [class.is-width-11]=\"field.width === 11\"\n [class.is-width-12]=\"(field.width ?? 12) === 12\"\n [class.is-selected]=\"selectedFieldId() === field.id\"\n [attr.data-form-builder-field-id]=\"field.id\"\n (click)=\"$event.stopPropagation(); selectCanvasField(field)\">\n <div class=\"ngs-form-builder-field-header\">\n <div class=\"ngs-form-builder-field-label\">\n <ngs-icon [name]=\"fieldIcon(field)\"/>\n <span>{{ field.label }}</span>\n </div>\n </div>\n\n <ngs-form-builder-field-host\n [field]=\"field\"\n [control]=\"previewControl(field)\"\n [definitions]=\"definitions()\"\n [readonly]=\"true\"\n [editableCanvas]=\"true\"/>\n </div>\n }\n } @else if (item.section; as section) {\n <ngs-card class=\"ngs-form-builder-section\"\n [attr.data-form-builder-section-id]=\"section.id\"\n (dragover)=\"nativeSectionDragOver($event, section)\"\n (dragleave)=\"nativeDragLeave($event, sectionDropListId(section))\"\n (drop)=\"nativeSectionDrop($event, section)\"\n (click)=\"selectSection(section)\">\n <ngs-card-header>\n <div class=\"ngs-form-builder-section-heading\">\n <input ngsInput\n class=\"ngs-form-builder-section-title\"\n [ngModel]=\"section.title\"\n [ngModelOptions]=\"{standalone: true}\"\n (ngModelChange)=\"updateSection(section, { title: $event })\"\n aria-label=\"Section title\">\n </div>\n @if (section.description) {\n <p class=\"truncate text-sm text-secondary\">{{ section.description }}</p>\n }\n\n <ngs-card-aside>\n <div class=\"ngs-form-builder-section-actions\">\n <button ngsIconButton type=\"button\" aria-label=\"Collapse section\" (click)=\"updateSection(section, { collapsed: !section.collapsed })\">\n <ngs-icon [name]=\"isSectionCollapsed(section) ? 'fluent:chevron-down-24-regular' : 'fluent:chevron-up-24-regular'\"/>\n </button>\n <button ngsIconButton type=\"button\" aria-label=\"Delete section\" (click)=\"removeSection(section)\">\n <ngs-icon name=\"fluent:delete-24-regular\"/>\n </button>\n </div>\n </ngs-card-aside>\n </ngs-card-header>\n\n @if (!isSectionCollapsed(section)) {\n <ngs-card-content>\n <ng-container\n [ngTemplateOutlet]=\"fieldList\"\n [ngTemplateOutletContext]=\"{\n $implicit: section.fields,\n containerId: sectionDropListId(section),\n section\n }\"/>\n </ngs-card-content>\n } @else if (isNativeDropTarget(sectionDropListId(section), section.fields.length)) {\n <ngs-card-content>\n <div class=\"ngs-form-builder-drop-list ngs-form-builder-canvas-drop-list\">\n <ng-container [ngTemplateOutlet]=\"nativeFieldGhost\"/>\n </div>\n </ngs-card-content>\n }\n </ngs-card>\n }\n }\n\n @if (isNativeDropTarget(rootDropListId(), canvasItems().length)) {\n <ng-container [ngTemplateOutlet]=\"nativeDragSection() ? nativeSectionGhost : nativeFieldGhost\"/>\n }\n\n </div>\n </div>\n </ngs-scrollbar-area>\n </ngs-panel-content>\n\n <ngs-panel-aside class=\"ngs-form-builder-inspector w-88 border-s border-s-subtle\">\n <ngs-scrollbar-area [absolute]=\"true\">\n <div class=\"flex flex-col gap-4 p-5\">\n <div class=\"flex items-start justify-between gap-3\">\n <div class=\"min-w-0\">\n <h3 class=\"text-base font-semibold\">{{ inspectorTitle() }}</h3>\n @if (selectedField() || selectedSection()) {\n <p class=\"truncate text-sm text-secondary\">{{ selectedField()?.label || selectedSection()?.title }}</p>\n }\n </div>\n <button ngsIconButton type=\"button\" aria-label=\"Clear selection\" (click)=\"selectedFieldId.set(null)\">\n <ngs-icon name=\"fluent:dismiss-24-regular\"/>\n </button>\n </div>\n\n @if (selectedField() || selectedSection()) {\n <ngs-form-builder-settings-host\n [field]=\"selectedField()\"\n [section]=\"selectedSection()\"\n [schema]=\"schema()\"\n [definitions]=\"definitions()\"\n [settingsDefinitions]=\"settingsDefinitions()\"\n [update]=\"updateSelectedField\"\n [updateSection]=\"updateSelectedSection\"/>\n\n @if (selectedField(); as field) {\n <button ngsButton=\"text\" type=\"button\" class=\"ngs-form-builder-delete-button\" (click)=\"confirmRemoveField(field)\">\n <ngs-icon name=\"fluent:delete-24-regular\"/>\n Delete field\n </button>\n }\n } @else {\n <div class=\"ngs-form-builder-empty-inspector\">\n Select a field on the canvas or in the fields tree to edit its settings.\n </div>\n }\n </div>\n </ngs-scrollbar-area>\n </ngs-panel-aside>\n</ngs-panel>\n\n<ng-template #previewDialog>\n <h3 ngs-dialog-title>{{ schema().title || 'Form preview' }}</h3>\n <ngs-dialog-content class=\"ngs-form-builder-preview-dialog-content\">\n <ngs-form-builder-renderer\n [schema]=\"schema()\"\n [showSubmit]=\"false\"/>\n </ngs-dialog-content>\n <ngs-dialog-actions align=\"end\">\n <button ngsButton=\"outlined\" type=\"button\" ngs-dialog-close>Close</button>\n </ngs-dialog-actions>\n</ng-template>\n", styles: [":host{display:block;min-height:640px}:host .ngs-form-builder-shell{height:100%;min-height:640px;overflow:hidden}:host .ngs-form-builder-palette-item{display:flex;width:100%;min-height:calc(var(--spacing, .25rem) * 12);align-items:center;border:1px solid var(--ngs-color-outline-variant);border-radius:var(--ngs-radius-lg);background:var(--ngs-color-surface);color:var(--ngs-color-on-surface);cursor:grab;font-size:var(--ngs-font-size-sm);font-weight:500;padding:0 calc(var(--spacing, .25rem) * 3);text-align:start;transition:background-color .16s cubic-bezier(0,0,.2,1),border-color .16s cubic-bezier(0,0,.2,1),box-shadow .16s cubic-bezier(0,0,.2,1)}:host .ngs-form-builder-palette-item:hover{border-color:var(--ngs-color-primary);background:var(--ngs-color-primary);color:var(--ngs-color-primary)}@supports (color: color-mix(in lab,red,red)){:host .ngs-form-builder-palette-item:hover{background:color-mix(in srgb,var(--ngs-color-primary),var(--ngs-color-surface) 96%)}}:host .ngs-form-builder-palette-item:focus-visible{outline:0;box-shadow:0 0 0 3px var(--ngs-state-focus-ring)}:host .ngs-form-builder-palette-item:active{cursor:grabbing}:host .ngs-form-builder-palette-item-preview{width:calc(var(--spacing, .25rem) * 64)}:host .ngs-form-builder-native-drag-image{position:fixed;inset-block-start:0;inset-inline-start:0;width:calc(var(--spacing, .25rem) * 64);pointer-events:none;transform:translate(-200vw,-200vh);z-index:-1}:host .ngs-form-builder-palette-item-placeholder{width:100%;box-shadow:none;opacity:1;pointer-events:none}:host .ngs-form-builder-palette{background:var(--ngs-color-surface-container-low)}:host .ngs-form-builder-palette-item-content{display:inline-flex;min-width:0;width:100%;align-items:center;justify-content:space-between;gap:calc(var(--spacing, .25rem) * 3)}:host .ngs-form-builder-palette-item-label{display:inline-flex;min-width:0;align-items:center;gap:calc(var(--spacing, .25rem) * 2)}:host .ngs-form-builder-palette-item-label span{min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}:host ngs-tab-group{--ngs-tab-label-height: calc(var(--spacing, .25rem) * 10)}:host ngs-tree{--ngs-tree-node-gap: calc(var(--spacing, .25rem) * 1)}:host ngs-tree ngs-tree-node{cursor:grab}:host ngs-tree ngs-tree-node:active{cursor:grabbing}:host .ngs-form-builder-tree-item{min-width:0;flex:1;justify-content:flex-start;overflow:hidden}:host .ngs-form-builder-tree-item span{min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}:host .ngs-form-builder-tree-item small{margin-inline-start:auto;color:var(--ngs-color-on-surface-variant);font-size:var(--ngs-font-size-xs);font-weight:400}:host .ngs-form-builder-tree-item.is-selected{background:var(--ngs-color-primary-container);color:var(--ngs-color-on-primary-container)}:host .ngs-form-builder-tree-item.is-selected small{color:currentColor}:host .ngs-form-builder-tree-drag-placeholder{display:flex;min-width:0;width:100%;min-height:calc(var(--spacing, .25rem) * 10);align-items:center;gap:calc(var(--spacing, .25rem) * 2);border:1px dashed var(--ngs-color-primary);border-radius:var(--ngs-radius-md);background:var(--ngs-color-primary);color:var(--ngs-color-primary);font-size:var(--ngs-font-size-sm);font-weight:500;padding:calc(var(--spacing, .25rem) * 2) calc(var(--spacing, .25rem) * 3);pointer-events:none}@supports (color: color-mix(in lab,red,red)){:host .ngs-form-builder-tree-drag-placeholder{background:color-mix(in srgb,var(--ngs-color-primary),var(--ngs-color-surface) 94%)}}:host .ngs-form-builder-tree-drag-placeholder ngs-icon{flex:none}:host .ngs-form-builder-tree-drag-placeholder span{min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}:host .ngs-form-builder-drag-icon{display:inline-flex;flex:none;align-items:center;color:var(--ngs-color-on-surface-variant)}:host .ngs-form-builder-canvas{min-height:100%}:host .ngs-form-builder-section{--ngs-card-padding: calc(var(--spacing, .25rem) * 4)}:host .ngs-form-builder-native-ghost-section{border-color:var(--ngs-color-primary);background:var(--ngs-color-primary);box-shadow:none;opacity:1;pointer-events:none}@supports (color: color-mix(in lab,red,red)){:host .ngs-form-builder-native-ghost-section{background:color-mix(in srgb,var(--ngs-color-primary),var(--ngs-color-surface) 94%)}}:host .ngs-form-builder-canvas-list{display:flex;flex-direction:column;gap:calc(var(--spacing, .25rem) * 4)}:host .ngs-form-builder-section-heading{display:flex;min-width:0;flex:1;align-items:center;gap:calc(var(--spacing, .25rem) * 3)}:host .ngs-form-builder-section-actions{display:flex;align-items:center;gap:calc(var(--spacing, .25rem) * 1)}:host .ngs-form-builder-section-title{width:100%;min-width:0;height:auto;min-height:0;padding:0;border:0;background:transparent;color:var(--ngs-color-on-surface);font-size:var(--ngs-font-size-base);font-weight:600;line-height:var(--ngs-line-height-base);outline:0}:host .ngs-form-builder-section-title:focus{outline:2px solid var(--ngs-color-primary);outline-offset:2px;border-radius:var(--ngs-radius-sm)}:host .ngs-form-builder-section-preview-title{min-width:0;overflow:hidden;color:var(--ngs-color-on-surface);font-size:var(--ngs-font-size-base);font-weight:600;line-height:var(--ngs-line-height-base);text-overflow:ellipsis;white-space:nowrap}:host .ngs-form-builder-drop-list{display:grid;grid-template-columns:repeat(12,minmax(0,1fr));gap:calc(var(--spacing, .25rem) * 4);min-height:calc(var(--spacing, .25rem) * 16)}:host .ngs-form-builder-field{position:relative;display:flex;flex-direction:column;grid-column:span 12;gap:calc(var(--spacing, .25rem) * 3);border:1px solid transparent;border-radius:var(--ngs-radius-lg);padding:calc(var(--spacing, .25rem) * 3);background:var(--ngs-color-surface);cursor:pointer}:host .ngs-form-builder-field.is-container{align-items:stretch;background:var(--ngs-color-surface-container-lowest)}:host .ngs-form-builder-field.is-width-1{grid-column:span 1}:host .ngs-form-builder-field.is-width-2{grid-column:span 2}:host .ngs-form-builder-field.is-width-3{grid-column:span 3}:host .ngs-form-builder-field.is-width-4{grid-column:span 4}:host .ngs-form-builder-field.is-width-5{grid-column:span 5}:host .ngs-form-builder-field.is-width-6{grid-column:span 6}:host .ngs-form-builder-field.is-width-7{grid-column:span 7}:host .ngs-form-builder-field.is-width-8{grid-column:span 8}:host .ngs-form-builder-field.is-width-9{grid-column:span 9}:host .ngs-form-builder-field.is-width-10{grid-column:span 10}:host .ngs-form-builder-field.is-width-11{grid-column:span 11}:host .ngs-form-builder-field.is-width-12{grid-column:span 12}:host .ngs-form-builder-field:hover{border-color:var(--ngs-color-outline-variant)}:host .ngs-form-builder-field.is-selected{border-color:var(--ngs-color-primary);box-shadow:0 0 0 1px var(--ngs-color-primary)}:host .ngs-form-builder-field-header{display:flex;min-width:0;align-items:center;justify-content:space-between;gap:calc(var(--spacing, .25rem) * 3)}:host .ngs-form-builder-field-label{display:flex;min-width:0;align-items:center;gap:calc(var(--spacing, .25rem) * 2);color:var(--ngs-color-on-surface);font-size:var(--ngs-font-size-sm);font-weight:600}:host .ngs-form-builder-field-label ngs-icon{flex:none;color:var(--ngs-color-primary)}:host .ngs-form-builder-field-label span{min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}:host .ngs-form-builder-field-host{min-width:0;width:100%;grid-column:auto}:host .ngs-form-builder-grid-field{display:flex;min-width:0;flex-direction:column;gap:calc(var(--spacing, .25rem) * 3)}:host .ngs-form-builder-grid-field-header{display:flex;align-items:center;justify-content:space-between;gap:calc(var(--spacing, .25rem) * 3);border:1px solid var(--ngs-color-outline-variant);border-radius:var(--ngs-radius-md);background:var(--ngs-color-surface);padding:calc(var(--spacing, .25rem) * 3)}:host .ngs-form-builder-grid-field-header p{overflow:hidden;color:var(--ngs-color-on-surface);font-size:var(--ngs-font-size-sm);font-weight:600;text-overflow:ellipsis;white-space:nowrap}:host .ngs-form-builder-grid-field-header span{display:block;overflow:hidden;color:var(--ngs-color-on-surface-variant);font-size:var(--ngs-font-size-xs);text-overflow:ellipsis;white-space:nowrap}:host .ngs-form-builder-empty-inspector{display:flex;min-height:calc(var(--spacing, .25rem) * 14);align-items:center;justify-content:center;border:1px dashed var(--ngs-color-primary);border-radius:var(--ngs-radius-lg);color:var(--ngs-color-on-surface-variant);font-size:var(--ngs-font-size-sm);grid-column:1/-1;padding:calc(var(--spacing, .25rem) * 4);text-align:center}:host .ngs-form-builder-empty-inspector{min-height:calc(var(--spacing, .25rem) * 28);border-color:var(--ngs-color-outline-variant)}:host .ngs-form-builder-delete-button{justify-content:flex-start;color:var(--ngs-color-danger)}:host .ngs-form-builder-preview-dialog-content{width:min(100%,760px)}:host .ngs-form-builder-field-placeholder{border-style:dashed;border-color:var(--ngs-color-primary);background:var(--ngs-color-primary);box-shadow:none;opacity:1;pointer-events:none}@supports (color: color-mix(in lab,red,red)){:host .ngs-form-builder-field-placeholder{background:color-mix(in srgb,var(--ngs-color-primary),var(--ngs-color-surface) 92%)}}:host .ngs-form-builder-ghost-control{height:calc(var(--spacing, .25rem) * 10);border:1px solid var(--ngs-color-outline-variant);border-radius:var(--ngs-radius-md);background:var(--ngs-color-surface)}\n/*! tailwindcss v4.2.2 | MIT License | https://tailwindcss.com */\n"] }]
1808
- }], propDecorators: { actualFieldsTree: [{ type: i0.ViewChild, args: ['actualFieldsTree', { isSignal: true }] }], schema: [{ type: i0.Input, args: [{ isSignal: true, alias: "schema", required: false }] }, { type: i0.Output, args: ["schemaChange"] }], paletteTitle: [{ type: i0.Input, args: [{ isSignal: true, alias: "paletteTitle", required: false }] }], inspectorTitle: [{ type: i0.Input, args: [{ isSignal: true, alias: "inspectorTitle", required: false }] }], fieldSelected: [{ type: i0.Output, args: ["fieldSelected"] }], fieldAdded: [{ type: i0.Output, args: ["fieldAdded"] }], fieldRemoved: [{ type: i0.Output, args: ["fieldRemoved"] }] } });
2205
+ }, template: "<ng-template #nativeFieldGhost>\n @if (nativeDragFieldDefinition(); as definition) {\n <div\n class=\"ngs-form-builder-field ngs-form-builder-field-placeholder ngs-form-builder-native-ghost-field\"\n [class.is-width-1]=\"definitionWidth(definition) === 1\"\n [class.is-width-2]=\"definitionWidth(definition) === 2\"\n [class.is-width-3]=\"definitionWidth(definition) === 3\"\n [class.is-width-4]=\"definitionWidth(definition) === 4\"\n [class.is-width-5]=\"definitionWidth(definition) === 5\"\n [class.is-width-6]=\"definitionWidth(definition) === 6\"\n [class.is-width-7]=\"definitionWidth(definition) === 7\"\n [class.is-width-8]=\"definitionWidth(definition) === 8\"\n [class.is-width-9]=\"definitionWidth(definition) === 9\"\n [class.is-width-10]=\"definitionWidth(definition) === 10\"\n [class.is-width-11]=\"definitionWidth(definition) === 11\"\n [class.is-width-12]=\"definitionWidth(definition) === 12\">\n <div class=\"ngs-form-builder-field-header\">\n <div class=\"ngs-form-builder-field-label\">\n <ngs-icon [name]=\"definition.icon || 'fluent:form-24-regular'\"/>\n <span>{{ definition.defaults?.label || definition.label }}</span>\n </div>\n <span class=\"ngs-form-builder-drag-icon\">\n <ngs-icon name=\"fluent:arrow-move-24-regular\"/>\n </span>\n </div>\n <div class=\"ngs-form-builder-ghost-control\"></div>\n </div>\n }\n</ng-template>\n\n<ng-template #nativeSectionGhost>\n <ngs-card class=\"ngs-form-builder-section ngs-form-builder-native-ghost-section\">\n <ngs-card-header>\n <div class=\"ngs-form-builder-section-heading\">\n <span class=\"ngs-form-builder-drag-icon\">\n <ngs-icon name=\"fluent:folder-add-24-regular\"/>\n </span>\n <span class=\"ngs-form-builder-section-title\">Section</span>\n </div>\n </ngs-card-header>\n </ngs-card>\n</ng-template>\n\n<ng-template #fieldList let-fields let-containerId=\"containerId\" let-section=\"section\">\n <div\n class=\"ngs-form-builder-drop-list ngs-form-builder-canvas-drop-list\"\n [id]=\"containerId\"\n (dragover)=\"nativeFieldDragOver($event, containerId)\"\n (dragleave)=\"nativeDragLeave($event, containerId)\"\n (drop)=\"nativeFieldDrop($event, containerId)\">\n @for (field of fields; track field.id; let fieldIndex = $index) {\n @if (isNativeDropTarget(containerId, fieldIndex)) {\n <ng-container [ngTemplateOutlet]=\"nativeFieldGhost\"/>\n }\n @if (isContainerField(field)) {\n <div\n class=\"ngs-form-builder-field is-container\"\n [class.is-width-1]=\"(field.width ?? 12) === 1\"\n [class.is-width-2]=\"field.width === 2\"\n [class.is-width-3]=\"field.width === 3\"\n [class.is-width-4]=\"field.width === 4\"\n [class.is-width-5]=\"field.width === 5\"\n [class.is-width-6]=\"field.width === 6\"\n [class.is-width-7]=\"field.width === 7\"\n [class.is-width-8]=\"field.width === 8\"\n [class.is-width-9]=\"field.width === 9\"\n [class.is-width-10]=\"field.width === 10\"\n [class.is-width-11]=\"field.width === 11\"\n [class.is-width-12]=\"(field.width ?? 12) === 12\"\n [class.is-selected]=\"selectedFieldId() === field.id\"\n [attr.data-form-builder-field-id]=\"field.id\"\n (dragover)=\"nativeContainerFieldDragOver($event, field)\"\n (dragleave)=\"nativeContainerFieldDragLeave($event, field)\"\n (drop)=\"nativeContainerFieldDrop($event, field)\"\n (click)=\"$event.stopPropagation(); selectCanvasField(field, section)\">\n <div class=\"ngs-form-builder-field-header\">\n <div class=\"ngs-form-builder-field-label\">\n <ngs-icon [name]=\"fieldIcon(field)\"/>\n <span>{{ field.label }}</span>\n </div>\n </div>\n\n <div class=\"ngs-form-builder-grid-field\">\n <ng-container\n [ngTemplateOutlet]=\"fieldList\"\n [ngTemplateOutletContext]=\"{\n $implicit: field.children ?? [],\n containerId: fieldDropListId(field),\n section\n }\"/>\n </div>\n </div>\n } @else {\n <div\n class=\"ngs-form-builder-field\"\n [class.is-width-1]=\"(field.width ?? 12) === 1\"\n [class.is-width-2]=\"field.width === 2\"\n [class.is-width-3]=\"field.width === 3\"\n [class.is-width-4]=\"field.width === 4\"\n [class.is-width-5]=\"field.width === 5\"\n [class.is-width-6]=\"field.width === 6\"\n [class.is-width-7]=\"field.width === 7\"\n [class.is-width-8]=\"field.width === 8\"\n [class.is-width-9]=\"field.width === 9\"\n [class.is-width-10]=\"field.width === 10\"\n [class.is-width-11]=\"field.width === 11\"\n [class.is-width-12]=\"(field.width ?? 12) === 12\"\n [class.is-selected]=\"selectedFieldId() === field.id\"\n [attr.data-form-builder-field-id]=\"field.id\"\n (click)=\"$event.stopPropagation(); selectCanvasField(field, section)\">\n <div class=\"ngs-form-builder-field-header\">\n <div class=\"ngs-form-builder-field-label\">\n <ngs-icon [name]=\"fieldIcon(field)\"/>\n <span>{{ field.label }}</span>\n </div>\n </div>\n\n <ngs-form-builder-field-host\n [field]=\"field\"\n [control]=\"previewControl(field)\"\n [definitions]=\"definitions()\"\n [uploadCallback]=\"uploadCallback()\"\n [readonly]=\"true\"\n [editableCanvas]=\"true\"/>\n </div>\n }\n }\n\n @if (isNativeDropTarget(containerId, fields.length)) {\n <ng-container [ngTemplateOutlet]=\"nativeFieldGhost\"/>\n }\n </div>\n</ng-template>\n\n<ngs-panel class=\"ngs-form-builder-shell\">\n <ngs-panel-header autoHeight class=\"flex items-center justify-between gap-4 border-b border-b-subtle px-5 py-3\">\n <div class=\"min-w-0\">\n <p class=\"text-sm text-secondary\">Form builder</p>\n <h2 class=\"truncate text-lg font-semibold\">{{ schema().title || 'Untitled form' }}</h2>\n </div>\n\n <div class=\"flex items-center gap-2\">\n <button ngsButton=\"outlined\" type=\"button\" (click)=\"openPreview(previewDialog)\">\n <ngs-icon name=\"fluent:eye-24-regular\"/>\n Preview\n </button>\n </div>\n </ngs-panel-header>\n\n <ngs-panel-sidebar class=\"ngs-form-builder-palette w-80 border-e border-e-subtle\">\n <ngs-tab-group\n animationDuration=\"0ms\"\n [selectedIndex]=\"fieldsTabIndex()\"\n (selectedIndexChange)=\"fieldsTabIndex.set($event)\">\n <ngs-tab label=\"Fields\" class=\"relative\">\n <ngs-scrollbar-area>\n <div class=\"flex flex-col gap-5 p-4\">\n <section class=\"flex flex-col gap-2\">\n <h4 class=\"text-xs font-semibold uppercase tracking-normal text-secondary\">Layout</h4>\n <button\n type=\"button\"\n class=\"ngs-form-builder-palette-item\"\n draggable=\"true\"\n (dragstart)=\"sectionPaletteDragStarted($event)\"\n (dragend)=\"paletteDragEnded()\">\n <span class=\"ngs-form-builder-palette-item-content\">\n <span class=\"ngs-form-builder-palette-item-label\">\n <ngs-icon name=\"fluent:folder-add-24-regular\"/>\n <span>Section</span>\n </span>\n </span>\n </button>\n\n @for (definition of layoutDefinitions(); track definition.type) {\n <button\n type=\"button\"\n class=\"ngs-form-builder-palette-item\"\n draggable=\"true\"\n (dragstart)=\"paletteDragStarted($event, definition)\"\n (dragend)=\"paletteDragEnded()\"\n (click)=\"paletteClicked(definition)\">\n <span class=\"ngs-form-builder-palette-item-content\">\n <span class=\"ngs-form-builder-palette-item-label\">\n <ngs-icon [name]=\"definition.icon || 'fluent:form-24-regular'\"/>\n <span>{{ definition.label }}</span>\n </span>\n </span>\n </button>\n }\n </section>\n\n @for (group of paletteGroups(); track group.name) {\n <section class=\"flex flex-col gap-2\">\n <h4 class=\"text-xs font-semibold uppercase tracking-normal text-secondary\">{{ group.name }}</h4>\n <div class=\"flex flex-col gap-2\">\n @for (definition of group.fields; track definition.type) {\n <button\n type=\"button\"\n class=\"ngs-form-builder-palette-item\"\n draggable=\"true\"\n (dragstart)=\"paletteDragStarted($event, definition)\"\n (dragend)=\"paletteDragEnded()\"\n (click)=\"paletteClicked(definition)\">\n <span class=\"ngs-form-builder-palette-item-content\">\n <span class=\"ngs-form-builder-palette-item-label\">\n <ngs-icon [name]=\"definition.icon || 'fluent:form-24-regular'\"/>\n <span>{{ definition.label }}</span>\n </span>\n </span>\n </button>\n }\n </div>\n </section>\n }\n </div>\n </ngs-scrollbar-area>\n </ngs-tab>\n <ngs-tab label=\"Layers\" class=\"relative\">\n <ngs-scrollbar-area>\n <div class=\"ps-1 pe-4 py-4\">\n <ngs-tree\n #actualFieldsTree=\"ngsTree\"\n [dataSource]=\"fieldTree()\"\n [childrenAccessor]=\"fieldTreeChildrenAccessor\"\n [trackBy]=\"trackFieldTreeNode\"\n [draggablePredicate]=\"fieldTreeDraggablePredicate\"\n [dropPredicate]=\"fieldTreeDropPredicate\"\n [nodePaddingIndent]=\"80\"\n [reorderOnDrop]=\"false\"\n (nodeDrop)=\"fieldTreeNodeDropped($event)\"\n draggable>\n <ng-template ngsTreeDragPlaceholder let-source=\"source\">\n <div class=\"ngs-form-builder-tree-drag-placeholder\">\n <ngs-icon [name]=\"fieldTreePlaceholderIcon(source)\"/>\n <span>{{ source.label }}</span>\n </div>\n </ng-template>\n\n <ngs-tree-node *ngsTreeNodeDef=\"let node\" ngsTreeNodePadding [value]=\"node.id\">\n <button\n ngsButton=\"text\"\n type=\"button\"\n class=\"ngs-form-builder-tree-item\"\n [class.is-selected]=\"selectedFieldId() === node.id\"\n [attr.data-form-builder-tree-node-id]=\"node.id\"\n (click)=\"selectFieldTreeNode(node)\">\n <ngs-icon [name]=\"node.icon\"/>\n {{ node.label }}\n<!-- <span class=\"inline-flex flex-col\">-->\n<!-- -->\n<!-- @if (node.name) {-->\n<!-- <small>{{ node.name }}</small>-->\n<!-- }-->\n<!-- </span>-->\n </button>\n </ngs-tree-node>\n\n <ngs-tree-node *ngsTreeNodeDef=\"let node; when: hasFieldTreeChildren\"\n ngsTreeNodePadding\n [value]=\"node.id\"\n [cdkTreeNodeTypeaheadLabel]=\"node.label\">\n <button\n ngsIconButton\n class=\"ngs-form-builder-tree-toggle\"\n [attr.aria-label]=\"'Toggle ' + node.label\"\n (click)=\"toggleFieldTreeNode(actualFieldsTree, node, $event)\">\n <ngs-icon [name]=\"isFieldTreeNodeExpanded(actualFieldsTree, node) ? 'fluent:chevron-down-24-regular' : 'fluent:chevron-right-24-regular'\" class=\"size-4\"/>\n </button>\n <button\n ngsButton=\"text\"\n type=\"button\"\n class=\"ngs-form-builder-tree-item\"\n [class.is-selected]=\"selectedFieldId() === node.id\"\n [attr.data-form-builder-tree-node-id]=\"node.id\"\n (click)=\"selectFieldTreeNode(node)\">\n <ngs-icon [name]=\"node.icon\"/>\n <span>{{ node.label }}</span>\n <!-- @if (node.name) {-->\n <!-- <small>{{ node.name }}</small>-->\n <!-- }-->\n </button>\n </ngs-tree-node>\n </ngs-tree>\n </div>\n </ngs-scrollbar-area>\n </ngs-tab>\n </ngs-tab-group>\n </ngs-panel-sidebar>\n\n <ngs-panel-content>\n <ngs-scrollbar-area [absolute]=\"true\">\n <div class=\"ngs-form-builder-canvas flex flex-col gap-4 p-5\">\n <div\n class=\"ngs-form-builder-canvas-list\"\n [id]=\"rootDropListId()\"\n (dragover)=\"nativeCanvasDragOver($event)\"\n (dragleave)=\"nativeDragLeave($event, rootDropListId())\"\n (drop)=\"nativeFieldDrop($event, rootDropListId())\">\n @for (item of canvasItems(); track item.kind + ':' + item.id; let itemIndex = $index) {\n @if (isNativeDropTarget(rootDropListId(), itemIndex)) {\n <ng-container [ngTemplateOutlet]=\"nativeDragSection() ? nativeSectionGhost : nativeFieldGhost\"/>\n }\n @if (item.field; as field) {\n @if (isContainerField(field)) {\n <div\n class=\"ngs-form-builder-field is-container\"\n [class.is-width-1]=\"(field.width ?? 12) === 1\"\n [class.is-width-2]=\"field.width === 2\"\n [class.is-width-3]=\"field.width === 3\"\n [class.is-width-4]=\"field.width === 4\"\n [class.is-width-5]=\"field.width === 5\"\n [class.is-width-6]=\"field.width === 6\"\n [class.is-width-7]=\"field.width === 7\"\n [class.is-width-8]=\"field.width === 8\"\n [class.is-width-9]=\"field.width === 9\"\n [class.is-width-10]=\"field.width === 10\"\n [class.is-width-11]=\"field.width === 11\"\n [class.is-width-12]=\"(field.width ?? 12) === 12\"\n [class.is-selected]=\"selectedFieldId() === field.id\"\n [attr.data-form-builder-field-id]=\"field.id\"\n (dragover)=\"nativeContainerFieldDragOver($event, field)\"\n (dragleave)=\"nativeContainerFieldDragLeave($event, field)\"\n (drop)=\"nativeContainerFieldDrop($event, field)\"\n (click)=\"$event.stopPropagation(); selectCanvasField(field)\">\n <div class=\"ngs-form-builder-field-header\">\n <div class=\"ngs-form-builder-field-label\">\n <ngs-icon [name]=\"fieldIcon(field)\"/>\n <span>{{ field.label }}</span>\n </div>\n </div>\n\n <div class=\"ngs-form-builder-grid-field\">\n <ng-container\n [ngTemplateOutlet]=\"fieldList\"\n [ngTemplateOutletContext]=\"{\n $implicit: field.children ?? [],\n containerId: fieldDropListId(field)\n }\"/>\n </div>\n </div>\n } @else {\n <div\n class=\"ngs-form-builder-field\"\n [class.is-width-1]=\"(field.width ?? 12) === 1\"\n [class.is-width-2]=\"field.width === 2\"\n [class.is-width-3]=\"field.width === 3\"\n [class.is-width-4]=\"field.width === 4\"\n [class.is-width-5]=\"field.width === 5\"\n [class.is-width-6]=\"field.width === 6\"\n [class.is-width-7]=\"field.width === 7\"\n [class.is-width-8]=\"field.width === 8\"\n [class.is-width-9]=\"field.width === 9\"\n [class.is-width-10]=\"field.width === 10\"\n [class.is-width-11]=\"field.width === 11\"\n [class.is-width-12]=\"(field.width ?? 12) === 12\"\n [class.is-selected]=\"selectedFieldId() === field.id\"\n [attr.data-form-builder-field-id]=\"field.id\"\n (click)=\"$event.stopPropagation(); selectCanvasField(field)\">\n <div class=\"ngs-form-builder-field-header\">\n <div class=\"ngs-form-builder-field-label\">\n <ngs-icon [name]=\"fieldIcon(field)\"/>\n <span>{{ field.label }}</span>\n </div>\n </div>\n\n <ngs-form-builder-field-host\n [field]=\"field\"\n [control]=\"previewControl(field)\"\n [definitions]=\"definitions()\"\n [uploadCallback]=\"uploadCallback()\"\n [readonly]=\"true\"\n [editableCanvas]=\"true\"/>\n </div>\n }\n } @else if (item.section; as section) {\n <ngs-card class=\"ngs-form-builder-section\"\n [attr.data-form-builder-section-id]=\"section.id\"\n (dragover)=\"nativeSectionDragOver($event, section)\"\n (dragleave)=\"nativeDragLeave($event, sectionDropListId(section))\"\n (drop)=\"nativeSectionDrop($event, section)\"\n (click)=\"selectSection(section)\">\n <ngs-card-header>\n <div class=\"ngs-form-builder-section-heading\">\n <input ngsInput\n class=\"ngs-form-builder-section-title\"\n [ngModel]=\"section.title\"\n [ngModelOptions]=\"{standalone: true}\"\n (ngModelChange)=\"updateSection(section, { title: $event })\"\n aria-label=\"Section title\">\n </div>\n @if (section.description) {\n <p class=\"truncate text-sm text-secondary\">{{ section.description }}</p>\n }\n\n <ngs-card-aside>\n <div class=\"ngs-form-builder-section-actions\">\n <button ngsIconButton type=\"button\" aria-label=\"Collapse section\" (click)=\"updateSection(section, { collapsed: !section.collapsed })\">\n <ngs-icon [name]=\"isSectionCollapsed(section) ? 'fluent:chevron-down-24-regular' : 'fluent:chevron-up-24-regular'\"/>\n </button>\n <button ngsIconButton type=\"button\" aria-label=\"Delete section\" (click)=\"removeSection(section)\">\n <ngs-icon name=\"fluent:delete-24-regular\"/>\n </button>\n </div>\n </ngs-card-aside>\n </ngs-card-header>\n\n @if (!isSectionCollapsed(section)) {\n <ngs-card-content>\n <ng-container\n [ngTemplateOutlet]=\"fieldList\"\n [ngTemplateOutletContext]=\"{\n $implicit: section.fields,\n containerId: sectionDropListId(section),\n section\n }\"/>\n </ngs-card-content>\n } @else if (isNativeDropTarget(sectionDropListId(section), section.fields.length)) {\n <ngs-card-content>\n <div class=\"ngs-form-builder-drop-list ngs-form-builder-canvas-drop-list\">\n <ng-container [ngTemplateOutlet]=\"nativeFieldGhost\"/>\n </div>\n </ngs-card-content>\n }\n </ngs-card>\n }\n }\n\n @if (isNativeDropTarget(rootDropListId(), canvasItems().length)) {\n <ng-container [ngTemplateOutlet]=\"nativeDragSection() ? nativeSectionGhost : nativeFieldGhost\"/>\n }\n\n </div>\n </div>\n </ngs-scrollbar-area>\n </ngs-panel-content>\n\n <ngs-panel-aside class=\"ngs-form-builder-inspector w-88 border-s border-s-subtle\">\n <ngs-scrollbar-area [absolute]=\"true\">\n <div class=\"flex flex-col gap-4 p-5\">\n <div class=\"flex items-start justify-between gap-3\">\n <div class=\"min-w-0\">\n <h3 class=\"text-base font-semibold\">{{ inspectorTitle() }}</h3>\n @if (selectedField() || selectedSection()) {\n <p class=\"truncate text-sm text-secondary\">{{ selectedField()?.label || selectedSection()?.title }}</p>\n }\n </div>\n <button ngsIconButton type=\"button\" aria-label=\"Clear selection\" (click)=\"selectedFieldId.set(null)\">\n <ngs-icon name=\"fluent:dismiss-24-regular\"/>\n </button>\n </div>\n\n @if (selectedField() || selectedSection()) {\n <ngs-form-builder-settings-host\n [field]=\"selectedField()\"\n [section]=\"selectedSection()\"\n [schema]=\"schema()\"\n [definitions]=\"definitions()\"\n [settingsDefinitions]=\"settingsDefinitions()\"\n [update]=\"updateSelectedField\"\n [updateSection]=\"updateSelectedSection\"/>\n\n @if (selectedField(); as field) {\n <div class=\"mt-3\">\n <button ngsButton=\"text\" type=\"button\" class=\"ngs-form-builder-delete-button\" (click)=\"confirmRemoveField(field)\">\n <ngs-icon name=\"fluent:delete-24-regular\"/>\n Delete field\n </button>\n </div>\n }\n } @else {\n <div class=\"ngs-form-builder-empty-inspector\">\n Select a field on the canvas or in the fields tree to edit its settings.\n </div>\n }\n </div>\n </ngs-scrollbar-area>\n </ngs-panel-aside>\n</ngs-panel>\n\n<ng-template #previewDialog>\n <h3 ngs-dialog-title>{{ schema().title || 'Form preview' }}</h3>\n <ngs-dialog-content class=\"ngs-form-builder-preview-dialog-content\">\n <ngs-form-renderer\n [schema]=\"schema()\"\n [uploadCallback]=\"uploadCallback()\"\n [showSubmit]=\"false\"/>\n </ngs-dialog-content>\n <ngs-dialog-actions align=\"end\">\n <button ngsButton=\"outlined\" type=\"button\" ngs-dialog-close>Close</button>\n </ngs-dialog-actions>\n</ng-template>\n", styles: [":host{display:block;min-height:640px}:host .ngs-form-builder-shell{height:100%;min-height:640px;overflow:hidden}:host .ngs-form-builder-palette-item{display:flex;width:100%;min-height:calc(var(--spacing, .25rem) * 12);align-items:center;border:1px solid var(--ngs-color-outline-variant);border-radius:var(--ngs-radius-lg);background:var(--ngs-color-surface);color:var(--ngs-color-on-surface);cursor:grab;font-size:var(--ngs-font-size-sm);font-weight:500;padding:0 calc(var(--spacing, .25rem) * 3);text-align:start;transition:background-color .16s cubic-bezier(0,0,.2,1),border-color .16s cubic-bezier(0,0,.2,1),box-shadow .16s cubic-bezier(0,0,.2,1)}:host .ngs-form-builder-palette-item:hover{border-color:var(--ngs-color-primary);background:var(--ngs-color-primary);color:var(--ngs-color-primary)}@supports (color: color-mix(in lab,red,red)){:host .ngs-form-builder-palette-item:hover{background:color-mix(in srgb,var(--ngs-color-primary),var(--ngs-color-surface) 96%)}}:host .ngs-form-builder-palette-item:focus-visible{outline:0;box-shadow:0 0 0 3px var(--ngs-state-focus-ring)}:host .ngs-form-builder-palette-item:active{cursor:grabbing}:host .ngs-form-builder-palette-item-preview{width:calc(var(--spacing, .25rem) * 64)}:host .ngs-form-builder-native-drag-image{position:fixed;inset-block-start:0;inset-inline-start:0;width:calc(var(--spacing, .25rem) * 64);pointer-events:none;transform:translate(-200vw,-200vh);z-index:-1}:host .ngs-form-builder-palette-item-placeholder{width:100%;box-shadow:none;opacity:1;pointer-events:none}:host .ngs-form-builder-palette{background:var(--ngs-color-surface-container-low)}:host .ngs-form-builder-palette-item-content{display:inline-flex;min-width:0;width:100%;align-items:center;justify-content:space-between;gap:calc(var(--spacing, .25rem) * 3)}:host .ngs-form-builder-palette-item-label{display:inline-flex;min-width:0;align-items:center;gap:calc(var(--spacing, .25rem) * 2)}:host .ngs-form-builder-palette-item-label span{min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}:host ngs-tab-group{--ngs-tab-label-height: calc(var(--spacing, .25rem) * 10)}:host ngs-tree{--ngs-tree-node-gap: calc(var(--spacing, .25rem) * 1)}:host ngs-tree ngs-tree-node{cursor:grab}:host ngs-tree ngs-tree-node:active{cursor:grabbing}:host .ngs-form-builder-tree-item{min-width:0;flex:1;justify-content:flex-start;overflow:hidden}:host .ngs-form-builder-tree-item span{min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}:host .ngs-form-builder-tree-item small{margin-inline-start:auto;color:var(--ngs-color-on-surface-variant);font-size:var(--ngs-font-size-xs);font-weight:400}:host .ngs-form-builder-tree-item.is-selected{background:var(--ngs-color-primary-container);color:var(--ngs-color-on-primary-container)}:host .ngs-form-builder-tree-item.is-selected small{color:currentColor}:host .ngs-form-builder-tree-drag-placeholder{display:flex;min-width:0;width:100%;min-height:calc(var(--spacing, .25rem) * 10);align-items:center;gap:calc(var(--spacing, .25rem) * 2);border:1px dashed var(--ngs-color-primary);border-radius:var(--ngs-radius-md);background:var(--ngs-color-primary);color:var(--ngs-color-primary);font-size:var(--ngs-font-size-sm);font-weight:500;padding:calc(var(--spacing, .25rem) * 2) calc(var(--spacing, .25rem) * 3);pointer-events:none}@supports (color: color-mix(in lab,red,red)){:host .ngs-form-builder-tree-drag-placeholder{background:color-mix(in srgb,var(--ngs-color-primary),var(--ngs-color-surface) 94%)}}:host .ngs-form-builder-tree-drag-placeholder ngs-icon{flex:none}:host .ngs-form-builder-tree-drag-placeholder span{min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}:host .ngs-form-builder-drag-icon{display:inline-flex;flex:none;align-items:center;color:var(--ngs-color-on-surface-variant)}:host .ngs-form-builder-canvas{min-height:100%}:host .ngs-form-builder-section{--ngs-card-padding: calc(var(--spacing, .25rem) * 4);grid-column:span 12}:host .ngs-form-builder-native-ghost-section{border-color:var(--ngs-color-primary);background:var(--ngs-color-primary);box-shadow:none;opacity:1;pointer-events:none}@supports (color: color-mix(in lab,red,red)){:host .ngs-form-builder-native-ghost-section{background:color-mix(in srgb,var(--ngs-color-primary),var(--ngs-color-surface) 94%)}}:host .ngs-form-builder-canvas-list{display:grid;grid-template-columns:repeat(12,minmax(0,1fr));gap:calc(var(--spacing, .25rem) * 4)}:host .ngs-form-builder-section-heading{display:flex;min-width:0;flex:1;align-items:center;gap:calc(var(--spacing, .25rem) * 3)}:host .ngs-form-builder-section-actions{display:flex;align-items:center;gap:calc(var(--spacing, .25rem) * 1)}:host .ngs-form-builder-section-title{width:100%;min-width:0;height:auto;min-height:0;padding:0;border:0;background:transparent;color:var(--ngs-color-on-surface);font-size:var(--ngs-font-size-base);font-weight:600;line-height:var(--ngs-line-height-base);outline:0}:host .ngs-form-builder-section-title:focus{outline:2px solid var(--ngs-color-primary);outline-offset:2px;border-radius:var(--ngs-radius-sm)}:host .ngs-form-builder-section-preview-title{min-width:0;overflow:hidden;color:var(--ngs-color-on-surface);font-size:var(--ngs-font-size-base);font-weight:600;line-height:var(--ngs-line-height-base);text-overflow:ellipsis;white-space:nowrap}:host .ngs-form-builder-drop-list{display:grid;grid-template-columns:repeat(12,minmax(0,1fr));gap:calc(var(--spacing, .25rem) * 4);min-height:calc(var(--spacing, .25rem) * 16)}:host .ngs-form-builder-field{position:relative;display:flex;flex-direction:column;grid-column:span 12;gap:calc(var(--spacing, .25rem) * 3);border:1px solid transparent;border-radius:var(--ngs-radius-lg);padding:calc(var(--spacing, .25rem) * 3);background:var(--ngs-color-surface);cursor:pointer}:host .ngs-form-builder-field.is-container{align-items:stretch;background:var(--ngs-color-surface-container-lowest)}:host .ngs-form-builder-field.is-width-1{grid-column:span 1}:host .ngs-form-builder-field.is-width-2{grid-column:span 2}:host .ngs-form-builder-field.is-width-3{grid-column:span 3}:host .ngs-form-builder-field.is-width-4{grid-column:span 4}:host .ngs-form-builder-field.is-width-5{grid-column:span 5}:host .ngs-form-builder-field.is-width-6{grid-column:span 6}:host .ngs-form-builder-field.is-width-7{grid-column:span 7}:host .ngs-form-builder-field.is-width-8{grid-column:span 8}:host .ngs-form-builder-field.is-width-9{grid-column:span 9}:host .ngs-form-builder-field.is-width-10{grid-column:span 10}:host .ngs-form-builder-field.is-width-11{grid-column:span 11}:host .ngs-form-builder-field.is-width-12{grid-column:span 12}:host .ngs-form-builder-field:hover{border-color:var(--ngs-color-outline-variant)}:host .ngs-form-builder-field.is-selected{border-color:var(--ngs-color-primary);box-shadow:0 0 0 1px var(--ngs-color-primary)}:host .ngs-form-builder-field-header{display:flex;min-width:0;align-items:center;justify-content:space-between;gap:calc(var(--spacing, .25rem) * 3)}:host .ngs-form-builder-field-label{display:flex;min-width:0;align-items:center;gap:calc(var(--spacing, .25rem) * 2);color:var(--ngs-color-on-surface);font-size:var(--ngs-font-size-sm);font-weight:600}:host .ngs-form-builder-field-label ngs-icon{flex:none;color:var(--ngs-color-primary)}:host .ngs-form-builder-field-label span{min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}:host .ngs-form-builder-field-host{min-width:0;width:100%;grid-column:auto}:host .ngs-form-builder-grid-field{display:flex;min-width:0;flex-direction:column;gap:calc(var(--spacing, .25rem) * 3)}:host .ngs-form-builder-grid-field-header{display:flex;align-items:center;justify-content:space-between;gap:calc(var(--spacing, .25rem) * 3);border:1px solid var(--ngs-color-outline-variant);border-radius:var(--ngs-radius-md);background:var(--ngs-color-surface);padding:calc(var(--spacing, .25rem) * 3)}:host .ngs-form-builder-grid-field-header p{overflow:hidden;color:var(--ngs-color-on-surface);font-size:var(--ngs-font-size-sm);font-weight:600;text-overflow:ellipsis;white-space:nowrap}:host .ngs-form-builder-grid-field-header span{display:block;overflow:hidden;color:var(--ngs-color-on-surface-variant);font-size:var(--ngs-font-size-xs);text-overflow:ellipsis;white-space:nowrap}:host .ngs-form-builder-empty-inspector{display:flex;min-height:calc(var(--spacing, .25rem) * 14);align-items:center;justify-content:center;border:1px dashed var(--ngs-color-primary);border-radius:var(--ngs-radius-lg);color:var(--ngs-color-on-surface-variant);font-size:var(--ngs-font-size-sm);grid-column:1/-1;padding:calc(var(--spacing, .25rem) * 4);text-align:center}:host .ngs-form-builder-empty-inspector{min-height:calc(var(--spacing, .25rem) * 28);border-color:var(--ngs-color-outline-variant)}:host .ngs-form-builder-delete-button{justify-content:flex-start;color:var(--ngs-color-danger)}:host .ngs-form-builder-preview-dialog-content{width:min(100%,760px)}:host .ngs-form-builder-field-placeholder{border-style:dashed;border-color:var(--ngs-color-primary);background:var(--ngs-color-primary);box-shadow:none;opacity:1;pointer-events:none}@supports (color: color-mix(in lab,red,red)){:host .ngs-form-builder-field-placeholder{background:color-mix(in srgb,var(--ngs-color-primary),var(--ngs-color-surface) 92%)}}:host .ngs-form-builder-ghost-control{height:calc(var(--spacing, .25rem) * 10);border:1px solid var(--ngs-color-outline-variant);border-radius:var(--ngs-radius-md);background:var(--ngs-color-surface)}\n/*! tailwindcss v4.2.2 | MIT License | https://tailwindcss.com */\n"] }]
2206
+ }], propDecorators: { actualFieldsTree: [{ type: i0.ViewChild, args: ['actualFieldsTree', { isSignal: true }] }], schema: [{ type: i0.Input, args: [{ isSignal: true, alias: "schema", required: false }] }, { type: i0.Output, args: ["schemaChange"] }], paletteTitle: [{ type: i0.Input, args: [{ isSignal: true, alias: "paletteTitle", required: false }] }], inspectorTitle: [{ type: i0.Input, args: [{ isSignal: true, alias: "inspectorTitle", required: false }] }], uploadCallback: [{ type: i0.Input, args: [{ isSignal: true, alias: "uploadCallback", required: false }] }], fieldSelected: [{ type: i0.Output, args: ["fieldSelected"] }], fieldAdded: [{ type: i0.Output, args: ["fieldAdded"] }], fieldRemoved: [{ type: i0.Output, args: ["fieldRemoved"] }] } });
1809
2207
  function createDefaultFormBuilderSchema() {
1810
2208
  const sectionId = uniqueId('section');
1811
2209
  return {
@@ -1912,5 +2310,5 @@ function containsField(field, fieldId) {
1912
2310
  * Generated bundle index. Do not edit.
1913
2311
  */
1914
2312
 
1915
- export { BasicFormBuilderFieldSettings, BasicFormBuilderLayoutSettings, BasicFormBuilderSectionSettings, DEFAULT_FORM_BUILDER_FIELDS, DEFAULT_FORM_BUILDER_ITEMS, FORM_BUILDER_FIELDS, FORM_BUILDER_ITEMS, FORM_BUILDER_SETTINGS, FormBuilder, FormBuilderFieldHost, FormBuilderRenderer, FormBuilderSettingsHost, FormBuilderRenderer as FormRenderer, createDefaultFormBuilderSchema, formBuilderField, formBuilderItem, formBuilderSettings, provideFormBuilder, validatorsFromRules };
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 };
1916
2314
  //# sourceMappingURL=ngstarter-ui-components-form-builder.mjs.map