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