@dynamic-field-kit/angular 1.3.3 → 1.5.0
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/CHANGELOG.md +185 -0
- package/LICENSE +21 -0
- package/README.md +193 -7
- package/dist/components/BaseInput.d.ts +5 -11
- package/dist/components/DynamicFormDevTools.d.ts +16 -0
- package/dist/components/DynamicInput.d.ts +8 -3
- package/dist/components/FieldInput.d.ts +12 -2
- package/dist/components/MultiFieldInput.d.ts +22 -2
- package/dist/fesm2022/dynamic-field-kit-angular.mjs +778 -87
- package/dist/fieldRegistryToken.d.ts +3 -0
- package/dist/index.d.ts +1 -1
- package/dist/layout/index.d.ts +2 -2
- package/dist/lib/dynamic-field-kit.module.d.ts +4 -4
- package/dist/lib/dynamic-form.store.d.ts +23 -0
- package/dist/public-api.d.ts +13 -8
- package/dist/types/layout.d.ts +4 -25
- package/package.json +46 -29
- package/angular.json +0 -27
- package/dist/README.md +0 -188
- package/dist/esm2022/components/BaseInput.mjs +0 -68
- package/dist/esm2022/components/DynamicInput.mjs +0 -191
- package/dist/esm2022/components/FieldInput.mjs +0 -80
- package/dist/esm2022/components/MultiFieldInput.mjs +0 -264
- package/dist/esm2022/dynamic-field-kit-angular.mjs +0 -5
- package/dist/esm2022/layout/defaultLayouts.mjs +0 -114
- package/dist/esm2022/layout/index.mjs +0 -3
- package/dist/esm2022/layout/layoutRegistry.mjs +0 -14
- package/dist/esm2022/lib/dynamic-field-kit.module.mjs +0 -52
- package/dist/esm2022/public-api.mjs +0 -14
- package/dist/esm2022/types/layout.mjs +0 -2
- package/dist/fesm2022/dynamic-field-kit-angular.mjs.map +0 -1
- package/karma.conf.js +0 -37
- package/ng-package.json +0 -7
- package/src/components/BaseInput.ts +0 -60
- package/src/components/DynamicInput.ts +0 -224
- package/src/components/FieldInput.ts +0 -67
- package/src/components/MultiFieldInput.ts +0 -238
- package/src/layout/defaultLayouts.ts +0 -70
- package/src/layout/index.ts +0 -2
- package/src/layout/layoutRegistry.ts +0 -25
- package/src/lib/dynamic-field-kit.module.ts +0 -29
- package/src/public-api.ts +0 -24
- package/src/types/layout.ts +0 -36
- package/test/DynamicInput.spec.ts +0 -30
- package/test/FieldInput.spec.ts +0 -44
- package/test/angular.spec.ts +0 -102
- package/test/integration.spec.ts +0 -57
- package/test/layouts.spec.ts +0 -26
- package/test.ts +0 -12
- package/tsconfig.json +0 -13
- package/tsconfig.spec.json +0 -21
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import * as i0 from '@angular/core';
|
|
2
|
-
import { EventEmitter, Output, Input, Component, ViewContainerRef, ViewChild, ChangeDetectionStrategy, HostListener, NgModule } from '@angular/core';
|
|
2
|
+
import { EventEmitter, Output, Input, Component, InjectionToken, inject, ViewContainerRef, ViewChild, ChangeDetectionStrategy, HostListener, signal, computed, NgModule } from '@angular/core';
|
|
3
3
|
import * as i1 from '@angular/common';
|
|
4
|
-
import { CommonModule, NgIf, NgClass, NgFor } from '@angular/common';
|
|
5
|
-
import { fieldRegistry, applyComputedValues, canAddGroupItem, canRemoveGroupItem, createGroupItem } from '@dynamic-field-kit/core';
|
|
6
|
-
export { fieldRegistry } from '@dynamic-field-kit/core';
|
|
4
|
+
import { CommonModule, NgIf, NgClass, NgFor, JsonPipe } from '@angular/common';
|
|
5
|
+
import { fieldRegistry, applyComputedValues, validateFields, resolveOptions, resolveDisabled, resolveReadOnly, validateField, canAddGroupItem, canRemoveGroupItem, createGroupItem } from '@dynamic-field-kit/core';
|
|
6
|
+
export { FieldRegistry, fieldRegistry, resolveDisabled, resolveOptions, resolveReadOnly, validateField, validateFieldAsync, validateFields, validateFieldsAsync, validators } from '@dynamic-field-kit/core';
|
|
7
7
|
|
|
8
8
|
class BaseInputComponent {
|
|
9
9
|
cdr;
|
|
@@ -12,14 +12,11 @@ class BaseInputComponent {
|
|
|
12
12
|
placeholder;
|
|
13
13
|
required;
|
|
14
14
|
disabled;
|
|
15
|
+
readOnly;
|
|
16
|
+
error;
|
|
15
17
|
options;
|
|
16
18
|
className;
|
|
17
19
|
description;
|
|
18
|
-
errorMessage;
|
|
19
|
-
acceptFile;
|
|
20
|
-
maxLength;
|
|
21
|
-
minNumber;
|
|
22
|
-
maxNumber;
|
|
23
20
|
valueChange = new EventEmitter();
|
|
24
21
|
constructor(cdr) {
|
|
25
22
|
this.cdr = cdr;
|
|
@@ -33,10 +30,10 @@ class BaseInputComponent {
|
|
|
33
30
|
hasChanges(changes, prop) {
|
|
34
31
|
return changes[prop] !== undefined;
|
|
35
32
|
}
|
|
36
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.
|
|
37
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.
|
|
33
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.25", ngImport: i0, type: BaseInputComponent, deps: [{ token: i0.ChangeDetectorRef }], target: i0.ɵɵFactoryTarget.Component });
|
|
34
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.25", type: BaseInputComponent, isStandalone: true, selector: "ng-component", inputs: { value: "value", label: "label", placeholder: "placeholder", required: "required", disabled: "disabled", readOnly: "readOnly", error: "error", options: "options", className: "className", description: "description" }, outputs: { valueChange: "valueChange" }, usesOnChanges: true, ngImport: i0, template: '', isInline: true });
|
|
38
35
|
}
|
|
39
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.
|
|
36
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.25", ngImport: i0, type: BaseInputComponent, decorators: [{
|
|
40
37
|
type: Component,
|
|
41
38
|
args: [{
|
|
42
39
|
template: '',
|
|
@@ -51,51 +48,65 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.20", ngImpo
|
|
|
51
48
|
type: Input
|
|
52
49
|
}], disabled: [{
|
|
53
50
|
type: Input
|
|
51
|
+
}], readOnly: [{
|
|
52
|
+
type: Input
|
|
53
|
+
}], error: [{
|
|
54
|
+
type: Input
|
|
54
55
|
}], options: [{
|
|
55
56
|
type: Input
|
|
56
57
|
}], className: [{
|
|
57
58
|
type: Input
|
|
58
59
|
}], description: [{
|
|
59
60
|
type: Input
|
|
60
|
-
}], errorMessage: [{
|
|
61
|
-
type: Input
|
|
62
|
-
}], acceptFile: [{
|
|
63
|
-
type: Input
|
|
64
|
-
}], maxLength: [{
|
|
65
|
-
type: Input
|
|
66
|
-
}], minNumber: [{
|
|
67
|
-
type: Input
|
|
68
|
-
}], maxNumber: [{
|
|
69
|
-
type: Input
|
|
70
61
|
}], valueChange: [{
|
|
71
62
|
type: Output
|
|
72
63
|
}] } });
|
|
73
64
|
|
|
65
|
+
// Injecting this token yields the process-wide singleton by default, so code
|
|
66
|
+
// that never provides it keeps working. Provide it on a component/route to give
|
|
67
|
+
// that subtree an isolated set of renderers:
|
|
68
|
+
//
|
|
69
|
+
// providers: [{ provide: FIELD_REGISTRY, useValue: myScopedRegistry }]
|
|
70
|
+
const FIELD_REGISTRY = new InjectionToken('dfk.FieldRegistry', {
|
|
71
|
+
providedIn: 'root',
|
|
72
|
+
factory: () => fieldRegistry,
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
// Only the framework-agnostic FieldRendererProps keys. Domain-specific props
|
|
76
|
+
// reach the renderer through `extraProps` (FieldDescription.props) instead.
|
|
74
77
|
const KNOWN_PROPS = [
|
|
75
78
|
'value',
|
|
76
79
|
'label',
|
|
77
80
|
'placeholder',
|
|
78
81
|
'required',
|
|
79
82
|
'disabled',
|
|
83
|
+
'readOnly',
|
|
84
|
+
'error',
|
|
80
85
|
'options',
|
|
81
86
|
'className',
|
|
82
87
|
'description',
|
|
83
|
-
'errorMessage',
|
|
84
|
-
'acceptFile',
|
|
85
|
-
'maxLength',
|
|
86
|
-
'minNumber',
|
|
87
|
-
'maxNumber',
|
|
88
88
|
];
|
|
89
89
|
class DynamicInput extends BaseInputComponent {
|
|
90
90
|
type;
|
|
91
|
+
// Extra, framework-agnostic props forwarded verbatim to the renderer.
|
|
92
|
+
extraProps;
|
|
91
93
|
valueChange = new EventEmitter();
|
|
92
94
|
onChange = new EventEmitter();
|
|
95
|
+
registry = inject(FIELD_REGISTRY);
|
|
93
96
|
host;
|
|
94
97
|
compRef;
|
|
95
98
|
inputInstance;
|
|
96
99
|
subscriptions = [];
|
|
100
|
+
// Tracks which KNOWN_PROPS were actually supplied to DynamicInput (via
|
|
101
|
+
// SimpleChanges), independent of TS class-field emit. See applyProps.
|
|
102
|
+
supplied = new Set();
|
|
97
103
|
ngOnChanges(changes) {
|
|
98
104
|
super.ngOnChanges(changes);
|
|
105
|
+
for (const prop of KNOWN_PROPS) {
|
|
106
|
+
if (changes[prop]) {
|
|
107
|
+
this.supplied.add(prop);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
99
110
|
if (this.inputInstance) {
|
|
100
111
|
this.syncPropsToInstance(changes);
|
|
101
112
|
}
|
|
@@ -121,6 +132,9 @@ class DynamicInput extends BaseInputComponent {
|
|
|
121
132
|
this.inputInstance[prop] = this[prop];
|
|
122
133
|
}
|
|
123
134
|
}
|
|
135
|
+
if (changes['extraProps']) {
|
|
136
|
+
this.applyExtraProps(this.inputInstance);
|
|
137
|
+
}
|
|
124
138
|
this.compRef?.changeDetectorRef?.detectChanges();
|
|
125
139
|
}
|
|
126
140
|
cleanup() {
|
|
@@ -137,10 +151,13 @@ class DynamicInput extends BaseInputComponent {
|
|
|
137
151
|
this.inputInstance = undefined;
|
|
138
152
|
}
|
|
139
153
|
render() {
|
|
140
|
-
const Renderer =
|
|
154
|
+
const Renderer = this.registry.get(this.type);
|
|
141
155
|
this.cleanup();
|
|
142
156
|
this.host.clear();
|
|
143
157
|
if (!Renderer) {
|
|
158
|
+
if (this.renderDefaultFallbackHTML5(this.type)) {
|
|
159
|
+
return;
|
|
160
|
+
}
|
|
144
161
|
this.renderError(`Unknown field type: ${this.type}`);
|
|
145
162
|
return;
|
|
146
163
|
}
|
|
@@ -154,8 +171,16 @@ class DynamicInput extends BaseInputComponent {
|
|
|
154
171
|
this.renderError(`Invalid renderer for type: ${this.type}`);
|
|
155
172
|
}
|
|
156
173
|
}
|
|
174
|
+
// Angular component classes carry a static ɵcmp. Checked instead of
|
|
175
|
+
// reflectComponentType() because the peer range starts at Angular 13 and
|
|
176
|
+
// reflectComponentType is v14+. Plain function renderers have no ɵcmp and
|
|
177
|
+
// fall through to renderFallback. Uses hasOwnProperty (not `in`) so a
|
|
178
|
+
// class that merely extends a component without its own @Component
|
|
179
|
+
// decorator - and would otherwise inherit the parent's static ɵcmp via the
|
|
180
|
+
// prototype chain - is not mistaken for a component in its own right.
|
|
157
181
|
isComponentType(renderer) {
|
|
158
|
-
return (typeof renderer === '
|
|
182
|
+
return (typeof renderer === 'function' &&
|
|
183
|
+
Object.prototype.hasOwnProperty.call(renderer, 'ɵcmp'));
|
|
159
184
|
}
|
|
160
185
|
renderComponent(compType) {
|
|
161
186
|
try {
|
|
@@ -191,25 +216,55 @@ class DynamicInput extends BaseInputComponent {
|
|
|
191
216
|
}
|
|
192
217
|
getFallbackProps() {
|
|
193
218
|
return {
|
|
219
|
+
...this.extraProps,
|
|
194
220
|
value: this.value,
|
|
195
221
|
onValueChange: (v) => this.emitValue(v),
|
|
196
222
|
label: this.label ?? '',
|
|
197
223
|
placeholder: this.placeholder ?? '',
|
|
198
224
|
required: this.required ?? false,
|
|
225
|
+
disabled: this.disabled ?? false,
|
|
226
|
+
readOnly: this.readOnly ?? false,
|
|
227
|
+
error: this.error,
|
|
199
228
|
options: this.options ?? [],
|
|
200
229
|
className: this.className ?? '',
|
|
201
230
|
description: this.description ?? '',
|
|
202
|
-
disabled: this.disabled ?? false,
|
|
203
|
-
errorMessage: this.errorMessage ?? '',
|
|
204
231
|
};
|
|
205
232
|
}
|
|
233
|
+
// Gates on `this.supplied` (populated from SimpleChanges in ngOnChanges),
|
|
234
|
+
// not on `prop in instanceObj` or `prop in this`. Both `in` checks test a
|
|
235
|
+
// TypeScript class-field emit artifact rather than intent: whether a
|
|
236
|
+
// property slot exists at runtime depends on the compile target
|
|
237
|
+
// (useDefineForClassFields is false below ES2022, true at ES2022+), so the
|
|
238
|
+
// same source can behave oppositely between this package's test build and
|
|
239
|
+
// its shipped ES2022 bundle, where every declared field is always an own
|
|
240
|
+
// property. `this.supplied` instead reflects only whether Angular ever
|
|
241
|
+
// fired a SimpleChange for that input, i.e. whether the prop was *bound*
|
|
242
|
+
// at all - it skips only props DynamicInput was never given a binding
|
|
243
|
+
// for. It is NOT a general "renderer defaults survive" guarantee: Angular
|
|
244
|
+
// fires a firstChange SimpleChange for every bound input even when the
|
|
245
|
+
// bound value is undefined, and FieldInput's template binds all
|
|
246
|
+
// KNOWN_PROPS unconditionally, so on the real
|
|
247
|
+
// MultiFieldInput -> FieldInput -> DynamicInput path every prop counts as
|
|
248
|
+
// supplied and renderer-side defaults (e.g. `@Input() label = 'None'`)
|
|
249
|
+
// are still overwritten with undefined. This only helps when DynamicInput
|
|
250
|
+
// is mounted directly with some inputs left unbound.
|
|
206
251
|
applyProps(instance) {
|
|
207
252
|
const instanceObj = instance;
|
|
208
253
|
for (const prop of KNOWN_PROPS) {
|
|
209
|
-
if (
|
|
254
|
+
if (this.supplied.has(prop)) {
|
|
210
255
|
instanceObj[prop] = this[prop];
|
|
211
256
|
}
|
|
212
257
|
}
|
|
258
|
+
this.applyExtraProps(instance);
|
|
259
|
+
}
|
|
260
|
+
applyExtraProps(instance) {
|
|
261
|
+
if (!instance || !this.extraProps) {
|
|
262
|
+
return;
|
|
263
|
+
}
|
|
264
|
+
const instanceObj = instance;
|
|
265
|
+
for (const [key, value] of Object.entries(this.extraProps)) {
|
|
266
|
+
instanceObj[key] = value;
|
|
267
|
+
}
|
|
213
268
|
}
|
|
214
269
|
bindOutputs(instance) {
|
|
215
270
|
const outputNames = [
|
|
@@ -228,16 +283,128 @@ class DynamicInput extends BaseInputComponent {
|
|
|
228
283
|
this.valueChange.emit(value);
|
|
229
284
|
this.onChange.emit(value);
|
|
230
285
|
}
|
|
286
|
+
renderDefaultFallbackHTML5(type) {
|
|
287
|
+
const nativeEl = this.host.element.nativeElement;
|
|
288
|
+
if (type === 'text' ||
|
|
289
|
+
type === 'password' ||
|
|
290
|
+
type === 'email' ||
|
|
291
|
+
type === 'number') {
|
|
292
|
+
const input = document.createElement('input');
|
|
293
|
+
input.type = type;
|
|
294
|
+
if (this.className) {
|
|
295
|
+
input.className = this.className;
|
|
296
|
+
}
|
|
297
|
+
if (this.placeholder) {
|
|
298
|
+
input.placeholder = this.placeholder;
|
|
299
|
+
}
|
|
300
|
+
if (this.disabled) {
|
|
301
|
+
input.disabled = true;
|
|
302
|
+
}
|
|
303
|
+
if (this.readOnly) {
|
|
304
|
+
input.readOnly = true;
|
|
305
|
+
}
|
|
306
|
+
if (this.required) {
|
|
307
|
+
input.required = true;
|
|
308
|
+
}
|
|
309
|
+
input.value = (this.value ?? '').toString();
|
|
310
|
+
input.addEventListener('input', (e) => {
|
|
311
|
+
const raw = e.target.value;
|
|
312
|
+
const val = type === 'number' ? (raw === '' ? undefined : Number(raw)) : raw;
|
|
313
|
+
this.emitValue(val);
|
|
314
|
+
});
|
|
315
|
+
nativeEl.appendChild(input);
|
|
316
|
+
return true;
|
|
317
|
+
}
|
|
318
|
+
if (type === 'textarea') {
|
|
319
|
+
const textarea = document.createElement('textarea');
|
|
320
|
+
if (this.className) {
|
|
321
|
+
textarea.className = this.className;
|
|
322
|
+
}
|
|
323
|
+
if (this.placeholder) {
|
|
324
|
+
textarea.placeholder = this.placeholder;
|
|
325
|
+
}
|
|
326
|
+
if (this.disabled) {
|
|
327
|
+
textarea.disabled = true;
|
|
328
|
+
}
|
|
329
|
+
if (this.readOnly) {
|
|
330
|
+
textarea.readOnly = true;
|
|
331
|
+
}
|
|
332
|
+
if (this.required) {
|
|
333
|
+
textarea.required = true;
|
|
334
|
+
}
|
|
335
|
+
textarea.value = this.value ?? '';
|
|
336
|
+
textarea.addEventListener('input', (e) => {
|
|
337
|
+
this.emitValue(e.target.value);
|
|
338
|
+
});
|
|
339
|
+
nativeEl.appendChild(textarea);
|
|
340
|
+
return true;
|
|
341
|
+
}
|
|
342
|
+
if (type === 'checkbox') {
|
|
343
|
+
const checkbox = document.createElement('input');
|
|
344
|
+
checkbox.type = 'checkbox';
|
|
345
|
+
if (this.className) {
|
|
346
|
+
checkbox.className = this.className;
|
|
347
|
+
}
|
|
348
|
+
if (this.disabled || this.readOnly) {
|
|
349
|
+
checkbox.disabled = true;
|
|
350
|
+
}
|
|
351
|
+
if (this.required) {
|
|
352
|
+
checkbox.required = true;
|
|
353
|
+
}
|
|
354
|
+
checkbox.checked = Boolean(this.value);
|
|
355
|
+
checkbox.addEventListener('change', (e) => {
|
|
356
|
+
this.emitValue(e.target.checked);
|
|
357
|
+
});
|
|
358
|
+
nativeEl.appendChild(checkbox);
|
|
359
|
+
return true;
|
|
360
|
+
}
|
|
361
|
+
if (type === 'select') {
|
|
362
|
+
const select = document.createElement('select');
|
|
363
|
+
if (this.className) {
|
|
364
|
+
select.className = this.className;
|
|
365
|
+
}
|
|
366
|
+
if (this.disabled || this.readOnly) {
|
|
367
|
+
select.disabled = true;
|
|
368
|
+
}
|
|
369
|
+
if (this.required) {
|
|
370
|
+
select.required = true;
|
|
371
|
+
}
|
|
372
|
+
const defaultOpt = document.createElement('option');
|
|
373
|
+
defaultOpt.value = '';
|
|
374
|
+
defaultOpt.disabled = true;
|
|
375
|
+
defaultOpt.textContent = '-- Select --';
|
|
376
|
+
select.appendChild(defaultOpt);
|
|
377
|
+
const options = this.options || [];
|
|
378
|
+
options.forEach((opt) => {
|
|
379
|
+
const optObj = opt;
|
|
380
|
+
const optVal = optObj['value'] ?? optObj['id'] ?? opt;
|
|
381
|
+
const optLabel = optObj['label'] ?? optObj['name'] ?? String(optVal);
|
|
382
|
+
const optionEl = document.createElement('option');
|
|
383
|
+
optionEl.value = String(optVal);
|
|
384
|
+
optionEl.textContent = String(optLabel);
|
|
385
|
+
if (String(optVal) === String(this.value)) {
|
|
386
|
+
optionEl.selected = true;
|
|
387
|
+
}
|
|
388
|
+
select.appendChild(optionEl);
|
|
389
|
+
});
|
|
390
|
+
select.addEventListener('change', (e) => {
|
|
391
|
+
this.emitValue(e.target.value);
|
|
392
|
+
});
|
|
393
|
+
nativeEl.appendChild(select);
|
|
394
|
+
return true;
|
|
395
|
+
}
|
|
396
|
+
return false;
|
|
397
|
+
}
|
|
231
398
|
renderError(message) {
|
|
232
399
|
const el = document.createElement('div');
|
|
233
400
|
el.textContent = message;
|
|
234
401
|
el.style.color = 'red';
|
|
235
402
|
this.host.element.nativeElement.appendChild(el);
|
|
236
403
|
}
|
|
237
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.
|
|
238
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.
|
|
404
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.25", ngImport: i0, type: DynamicInput, deps: null, target: i0.ɵɵFactoryTarget.Component });
|
|
405
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.25", type: DynamicInput, isStandalone: true, selector: "dfk-dynamic-input", inputs: { type: "type", extraProps: "extraProps" }, outputs: { valueChange: "valueChange", onChange: "onChange" }, viewQueries: [{ propertyName: "host", first: true, predicate: ["host"], descendants: true, read: ViewContainerRef }], usesInheritance: true, usesOnChanges: true, ngImport: i0, template: `<div #host style="display: contents;"></div>`, isInline: true, dependencies: [{ kind: "ngmodule", type: CommonModule }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
239
406
|
}
|
|
240
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.
|
|
407
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.25", ngImport: i0, type: DynamicInput, decorators: [{
|
|
241
408
|
type: Component,
|
|
242
409
|
args: [{
|
|
243
410
|
selector: 'dfk-dynamic-input',
|
|
@@ -248,6 +415,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.20", ngImpo
|
|
|
248
415
|
}]
|
|
249
416
|
}], propDecorators: { type: [{
|
|
250
417
|
type: Input
|
|
418
|
+
}], extraProps: [{
|
|
419
|
+
type: Input
|
|
251
420
|
}], valueChange: [{
|
|
252
421
|
type: Output
|
|
253
422
|
}], onChange: [{
|
|
@@ -261,8 +430,30 @@ class FieldInput {
|
|
|
261
430
|
cdr;
|
|
262
431
|
fieldDescription;
|
|
263
432
|
value;
|
|
433
|
+
options;
|
|
434
|
+
disabled;
|
|
435
|
+
readOnly;
|
|
436
|
+
error;
|
|
264
437
|
onValueChangeField = new EventEmitter();
|
|
438
|
+
/**
|
|
439
|
+
* Emits this field's name when focus leaves it. Driven by `focusout`, which
|
|
440
|
+
* bubbles, so it works for any renderer without the renderer having to
|
|
441
|
+
* declare a blur output of its own.
|
|
442
|
+
*/
|
|
443
|
+
onBlurField = new EventEmitter();
|
|
265
444
|
shouldRender = false;
|
|
445
|
+
get resolvedOptions() {
|
|
446
|
+
if (this.options) {
|
|
447
|
+
return this.options;
|
|
448
|
+
}
|
|
449
|
+
if (!this.fieldDescription?.options) {
|
|
450
|
+
return undefined;
|
|
451
|
+
}
|
|
452
|
+
if (typeof this.fieldDescription.options === 'function') {
|
|
453
|
+
return undefined;
|
|
454
|
+
}
|
|
455
|
+
return this.fieldDescription.options;
|
|
456
|
+
}
|
|
266
457
|
constructor(cdr) {
|
|
267
458
|
this.cdr = cdr;
|
|
268
459
|
}
|
|
@@ -270,36 +461,30 @@ class FieldInput {
|
|
|
270
461
|
this.shouldRender = !!this.fieldDescription;
|
|
271
462
|
this.cdr.markForCheck();
|
|
272
463
|
}
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
return undefined;
|
|
276
|
-
}
|
|
277
|
-
if (this.value === undefined && this.fieldDescription.type === 'text') {
|
|
278
|
-
return '';
|
|
279
|
-
}
|
|
280
|
-
return this.value;
|
|
281
|
-
}
|
|
282
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.20", ngImport: i0, type: FieldInput, deps: [{ token: i0.ChangeDetectorRef }], target: i0.ɵɵFactoryTarget.Component });
|
|
283
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.20", type: FieldInput, isStandalone: true, selector: "dfk-field-input", inputs: { fieldDescription: "fieldDescription", value: "value" }, outputs: { onValueChangeField: "onValueChangeField" }, usesOnChanges: true, ngImport: i0, template: `
|
|
464
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.25", ngImport: i0, type: FieldInput, deps: [{ token: i0.ChangeDetectorRef }], target: i0.ɵɵFactoryTarget.Component });
|
|
465
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.25", type: FieldInput, isStandalone: true, selector: "dfk-field-input", inputs: { fieldDescription: "fieldDescription", value: "value", options: "options", disabled: "disabled", readOnly: "readOnly", error: "error" }, outputs: { onValueChangeField: "onValueChangeField", onBlurField: "onBlurField" }, usesOnChanges: true, ngImport: i0, template: `
|
|
284
466
|
<dfk-dynamic-input
|
|
285
467
|
*ngIf="shouldRender"
|
|
286
468
|
[type]="fieldDescription!.type"
|
|
287
|
-
[value]="
|
|
469
|
+
[value]="value"
|
|
288
470
|
[label]="fieldDescription!.label"
|
|
289
471
|
[placeholder]="fieldDescription!.placeholder"
|
|
290
472
|
[required]="fieldDescription!.required"
|
|
291
473
|
[description]="$any(fieldDescription!.description)"
|
|
292
|
-
[options]="
|
|
474
|
+
[options]="resolvedOptions"
|
|
293
475
|
[className]="fieldDescription!.className"
|
|
294
476
|
(valueChange)="
|
|
295
477
|
onValueChangeField.emit({ value: $event, key: fieldDescription!.name })
|
|
296
478
|
"
|
|
297
|
-
|
|
298
|
-
[
|
|
479
|
+
(focusout)="onBlurField.emit(fieldDescription!.name)"
|
|
480
|
+
[disabled]="disabled"
|
|
481
|
+
[readOnly]="readOnly"
|
|
482
|
+
[error]="$any(error)"
|
|
483
|
+
[extraProps]="fieldDescription!.props"
|
|
299
484
|
></dfk-dynamic-input>
|
|
300
|
-
`, isInline: true, dependencies: [{ kind: "directive", type: NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "component", type: DynamicInput, selector: "dfk-dynamic-input", inputs: ["type"], outputs: ["valueChange", "onChange"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
485
|
+
`, isInline: true, dependencies: [{ kind: "directive", type: NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "component", type: DynamicInput, selector: "dfk-dynamic-input", inputs: ["type", "extraProps"], outputs: ["valueChange", "onChange"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
301
486
|
}
|
|
302
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.
|
|
487
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.25", ngImport: i0, type: FieldInput, decorators: [{
|
|
303
488
|
type: Component,
|
|
304
489
|
args: [{
|
|
305
490
|
selector: 'dfk-field-input',
|
|
@@ -310,18 +495,21 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.20", ngImpo
|
|
|
310
495
|
<dfk-dynamic-input
|
|
311
496
|
*ngIf="shouldRender"
|
|
312
497
|
[type]="fieldDescription!.type"
|
|
313
|
-
[value]="
|
|
498
|
+
[value]="value"
|
|
314
499
|
[label]="fieldDescription!.label"
|
|
315
500
|
[placeholder]="fieldDescription!.placeholder"
|
|
316
501
|
[required]="fieldDescription!.required"
|
|
317
502
|
[description]="$any(fieldDescription!.description)"
|
|
318
|
-
[options]="
|
|
503
|
+
[options]="resolvedOptions"
|
|
319
504
|
[className]="fieldDescription!.className"
|
|
320
505
|
(valueChange)="
|
|
321
506
|
onValueChangeField.emit({ value: $event, key: fieldDescription!.name })
|
|
322
507
|
"
|
|
323
|
-
|
|
324
|
-
[
|
|
508
|
+
(focusout)="onBlurField.emit(fieldDescription!.name)"
|
|
509
|
+
[disabled]="disabled"
|
|
510
|
+
[readOnly]="readOnly"
|
|
511
|
+
[error]="$any(error)"
|
|
512
|
+
[extraProps]="fieldDescription!.props"
|
|
325
513
|
></dfk-dynamic-input>
|
|
326
514
|
`,
|
|
327
515
|
}]
|
|
@@ -329,8 +517,18 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.20", ngImpo
|
|
|
329
517
|
type: Input
|
|
330
518
|
}], value: [{
|
|
331
519
|
type: Input
|
|
520
|
+
}], options: [{
|
|
521
|
+
type: Input
|
|
522
|
+
}], disabled: [{
|
|
523
|
+
type: Input
|
|
524
|
+
}], readOnly: [{
|
|
525
|
+
type: Input
|
|
526
|
+
}], error: [{
|
|
527
|
+
type: Input
|
|
332
528
|
}], onValueChangeField: [{
|
|
333
529
|
type: Output
|
|
530
|
+
}], onBlurField: [{
|
|
531
|
+
type: Output
|
|
334
532
|
}] } });
|
|
335
533
|
|
|
336
534
|
const DEFAULT_BREAKPOINT = 768;
|
|
@@ -339,7 +537,28 @@ class MultiFieldInput {
|
|
|
339
537
|
fieldDescriptions = [];
|
|
340
538
|
properties;
|
|
341
539
|
onChange = new EventEmitter();
|
|
540
|
+
validityChange = new EventEmitter();
|
|
541
|
+
/**
|
|
542
|
+
* Emits a field's name when it loses focus. Touched state is tracked
|
|
543
|
+
* internally either way; this is the hook for driving an external form store
|
|
544
|
+
* - pass `createDynamicFormStore`'s `handleBlur` to get its `touched` map and
|
|
545
|
+
* `validateOnBlur` behaviour.
|
|
546
|
+
*/
|
|
547
|
+
onBlurField = new EventEmitter();
|
|
548
|
+
touchedFields = {};
|
|
549
|
+
handleBlurField(fieldName) {
|
|
550
|
+
this.touchedFields = { ...this.touchedFields, [fieldName]: true };
|
|
551
|
+
this.onBlurField.emit(fieldName);
|
|
552
|
+
}
|
|
553
|
+
/** Whether this field has been blurred at least once. */
|
|
554
|
+
isTouched(fieldName) {
|
|
555
|
+
return Boolean(this.touchedFields[fieldName]);
|
|
556
|
+
}
|
|
342
557
|
layout = 'column';
|
|
558
|
+
// Top-level form data, threaded down through repeatable groups so a nested
|
|
559
|
+
// field's appearCondition/computeValue can read the root form. Omitted at
|
|
560
|
+
// the top level, where the form's own data is the root.
|
|
561
|
+
rootData;
|
|
343
562
|
data = {};
|
|
344
563
|
visibleFields = [];
|
|
345
564
|
isMobile = false;
|
|
@@ -383,6 +602,18 @@ class MultiFieldInput {
|
|
|
383
602
|
trackByIndex(index) {
|
|
384
603
|
return index;
|
|
385
604
|
}
|
|
605
|
+
groupTrackByCache = new WeakMap();
|
|
606
|
+
// Returns a stable trackBy per group field: item[keyField] when configured,
|
|
607
|
+
// else the index. Cached by field reference so the template hands Angular the
|
|
608
|
+
// same function each change-detection pass.
|
|
609
|
+
groupTrackBy(field) {
|
|
610
|
+
let fn = this.groupTrackByCache.get(field);
|
|
611
|
+
if (!fn) {
|
|
612
|
+
fn = (index, item) => field.keyField ? (item[field.keyField] ?? index) : index;
|
|
613
|
+
this.groupTrackByCache.set(field, fn);
|
|
614
|
+
}
|
|
615
|
+
return fn;
|
|
616
|
+
}
|
|
386
617
|
ngOnInit() {
|
|
387
618
|
this.updateIsMobile();
|
|
388
619
|
this.init();
|
|
@@ -392,21 +623,27 @@ class MultiFieldInput {
|
|
|
392
623
|
}
|
|
393
624
|
updateIsMobile() {
|
|
394
625
|
const breakpoint = typeof this.layout === 'object' && this.layout.type === 'responsive'
|
|
395
|
-
? this.layout.breakpoint ?? DEFAULT_BREAKPOINT
|
|
626
|
+
? (this.layout.breakpoint ?? DEFAULT_BREAKPOINT)
|
|
396
627
|
: DEFAULT_BREAKPOINT;
|
|
397
|
-
|
|
398
|
-
typeof window
|
|
628
|
+
if (typeof window !== 'undefined' &&
|
|
629
|
+
typeof window.matchMedia === 'function') {
|
|
630
|
+
this.isMobile = window.matchMedia(`(max-width: ${breakpoint - 1}px)`).matches;
|
|
631
|
+
}
|
|
632
|
+
else {
|
|
633
|
+
this.isMobile =
|
|
634
|
+
typeof window !== 'undefined' && window.innerWidth < breakpoint;
|
|
635
|
+
}
|
|
399
636
|
}
|
|
400
637
|
init() {
|
|
401
638
|
if (this.properties) {
|
|
402
|
-
this.data = applyComputedValues(this.fieldDescriptions, {
|
|
403
|
-
...this.properties,
|
|
404
|
-
});
|
|
639
|
+
this.data = applyComputedValues(this.fieldDescriptions, { ...this.properties }, this.rootData);
|
|
405
640
|
}
|
|
406
641
|
this.updateVisibleFields();
|
|
642
|
+
this.validityChange.emit(validateFields(this.fieldDescriptions, this.data, this.rootData));
|
|
407
643
|
}
|
|
408
644
|
updateVisibleFields() {
|
|
409
|
-
|
|
645
|
+
const root = this.rootData ?? this.data;
|
|
646
|
+
this.visibleFields = this.fieldDescriptions.filter((f) => !f.appearCondition || f.appearCondition(this.data, root));
|
|
410
647
|
}
|
|
411
648
|
onFieldChange(event) {
|
|
412
649
|
this.commitData({ ...this.data, [event.key]: event.value });
|
|
@@ -415,6 +652,22 @@ class MultiFieldInput {
|
|
|
415
652
|
const value = this.data[field.name];
|
|
416
653
|
return Array.isArray(value) ? value : [];
|
|
417
654
|
}
|
|
655
|
+
getResolvedOptions(field) {
|
|
656
|
+
return resolveOptions(field, this.data, this.rootData);
|
|
657
|
+
}
|
|
658
|
+
getDisabled(field) {
|
|
659
|
+
return resolveDisabled(field, this.data, this.rootData);
|
|
660
|
+
}
|
|
661
|
+
getReadOnly(field) {
|
|
662
|
+
return resolveReadOnly(field, this.data, this.rootData);
|
|
663
|
+
}
|
|
664
|
+
getError(field) {
|
|
665
|
+
if (this.getDisabled(field)) {
|
|
666
|
+
return undefined;
|
|
667
|
+
}
|
|
668
|
+
const errors = validateField(field, this.data[field.name], this.data, this.rootData);
|
|
669
|
+
return errors.length > 0 ? errors : undefined;
|
|
670
|
+
}
|
|
418
671
|
canAddItem(field) {
|
|
419
672
|
return canAddGroupItem(field, this.getItems(field));
|
|
420
673
|
}
|
|
@@ -444,19 +697,20 @@ class MultiFieldInput {
|
|
|
444
697
|
this.commitData({ ...this.data, [field.name]: items });
|
|
445
698
|
}
|
|
446
699
|
commitData(nextData) {
|
|
447
|
-
this.data = applyComputedValues(this.fieldDescriptions, nextData);
|
|
700
|
+
this.data = applyComputedValues(this.fieldDescriptions, nextData, this.rootData);
|
|
448
701
|
this.updateVisibleFields();
|
|
449
702
|
this.onChange.emit(this.data);
|
|
703
|
+
this.validityChange.emit(validateFields(this.fieldDescriptions, this.data, this.rootData));
|
|
450
704
|
this.cdr.markForCheck();
|
|
451
705
|
}
|
|
452
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.
|
|
453
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.
|
|
706
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.25", ngImport: i0, type: MultiFieldInput, deps: [{ token: i0.ChangeDetectorRef }], target: i0.ɵɵFactoryTarget.Component });
|
|
707
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.25", type: MultiFieldInput, isStandalone: true, selector: "dfk-multi-field-input", inputs: { fieldDescriptions: "fieldDescriptions", properties: "properties", layout: "layout", rootData: "rootData" }, outputs: { onChange: "onChange", validityChange: "validityChange", onBlurField: "onBlurField" }, host: { listeners: { "window:resize": "onWindowResize()" } }, usesOnChanges: true, ngImport: i0, template: `
|
|
454
708
|
<div
|
|
455
709
|
class="p-4 border rounded-lg bg-gray-50"
|
|
456
710
|
[ngClass]="{
|
|
457
711
|
'flex flex-col': resolvedLayoutType === 'column',
|
|
458
712
|
'flex flex-row': resolvedLayoutType === 'row',
|
|
459
|
-
grid: resolvedLayoutType === 'grid'
|
|
713
|
+
grid: resolvedLayoutType === 'grid',
|
|
460
714
|
}"
|
|
461
715
|
[style.gap.px]="gap"
|
|
462
716
|
[style.gridTemplateColumns]="
|
|
@@ -468,7 +722,12 @@ class MultiFieldInput {
|
|
|
468
722
|
*ngIf="!field.fields"
|
|
469
723
|
[fieldDescription]="field"
|
|
470
724
|
[value]="data[field.name]"
|
|
725
|
+
[options]="getResolvedOptions(field)"
|
|
726
|
+
[disabled]="getDisabled(field)"
|
|
727
|
+
[readOnly]="getReadOnly(field)"
|
|
728
|
+
[error]="getError(field)"
|
|
471
729
|
(onValueChangeField)="onFieldChange($event)"
|
|
730
|
+
(onBlurField)="handleBlurField($event)"
|
|
472
731
|
></dfk-field-input>
|
|
473
732
|
|
|
474
733
|
<div *ngIf="field.fields" [class]="field.className">
|
|
@@ -477,7 +736,7 @@ class MultiFieldInput {
|
|
|
477
736
|
*ngFor="
|
|
478
737
|
let item of getItems(field);
|
|
479
738
|
let i = index;
|
|
480
|
-
trackBy:
|
|
739
|
+
trackBy: groupTrackBy(field)
|
|
481
740
|
"
|
|
482
741
|
style="display: flex; align-items: flex-start; gap: 8px;"
|
|
483
742
|
>
|
|
@@ -485,11 +744,19 @@ class MultiFieldInput {
|
|
|
485
744
|
<dfk-multi-field-input
|
|
486
745
|
[fieldDescriptions]="field.fields"
|
|
487
746
|
[properties]="item"
|
|
747
|
+
[rootData]="rootData ?? data"
|
|
488
748
|
(onChange)="onGroupItemChange(field, i, $event)"
|
|
489
749
|
></dfk-multi-field-input>
|
|
490
750
|
</div>
|
|
491
751
|
<button
|
|
492
752
|
type="button"
|
|
753
|
+
[attr.aria-label]="
|
|
754
|
+
(field.removeLabel || 'Remove') +
|
|
755
|
+
' ' +
|
|
756
|
+
(field.label || field.name) +
|
|
757
|
+
' ' +
|
|
758
|
+
(i + 1)
|
|
759
|
+
"
|
|
493
760
|
(click)="onGroupItemRemove(field, i)"
|
|
494
761
|
[disabled]="!canRemoveItem(field)"
|
|
495
762
|
>
|
|
@@ -498,6 +765,9 @@ class MultiFieldInput {
|
|
|
498
765
|
</div>
|
|
499
766
|
<button
|
|
500
767
|
type="button"
|
|
768
|
+
[attr.aria-label]="
|
|
769
|
+
(field.addLabel || 'Add') + ' ' + (field.label || field.name)
|
|
770
|
+
"
|
|
501
771
|
(click)="onGroupItemAdd(field)"
|
|
502
772
|
[disabled]="!canAddItem(field)"
|
|
503
773
|
>
|
|
@@ -506,9 +776,9 @@ class MultiFieldInput {
|
|
|
506
776
|
</div>
|
|
507
777
|
</ng-container>
|
|
508
778
|
</div>
|
|
509
|
-
`, isInline: true, dependencies: [{ kind: "component", type: MultiFieldInput, selector: "dfk-multi-field-input", inputs: ["fieldDescriptions", "properties", "layout"], outputs: ["onChange"] }, { kind: "directive", type: NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: NgFor, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "component", type: FieldInput, selector: "dfk-field-input", inputs: ["fieldDescription", "value"], outputs: ["onValueChangeField"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
779
|
+
`, isInline: true, dependencies: [{ kind: "component", type: MultiFieldInput, selector: "dfk-multi-field-input", inputs: ["fieldDescriptions", "properties", "layout", "rootData"], outputs: ["onChange", "validityChange", "onBlurField"] }, { kind: "directive", type: NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: NgFor, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "component", type: FieldInput, selector: "dfk-field-input", inputs: ["fieldDescription", "value", "options", "disabled", "readOnly", "error"], outputs: ["onValueChangeField", "onBlurField"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
510
780
|
}
|
|
511
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.
|
|
781
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.25", ngImport: i0, type: MultiFieldInput, decorators: [{
|
|
512
782
|
type: Component,
|
|
513
783
|
args: [{
|
|
514
784
|
selector: 'dfk-multi-field-input',
|
|
@@ -527,7 +797,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.20", ngImpo
|
|
|
527
797
|
[ngClass]="{
|
|
528
798
|
'flex flex-col': resolvedLayoutType === 'column',
|
|
529
799
|
'flex flex-row': resolvedLayoutType === 'row',
|
|
530
|
-
grid: resolvedLayoutType === 'grid'
|
|
800
|
+
grid: resolvedLayoutType === 'grid',
|
|
531
801
|
}"
|
|
532
802
|
[style.gap.px]="gap"
|
|
533
803
|
[style.gridTemplateColumns]="
|
|
@@ -539,7 +809,12 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.20", ngImpo
|
|
|
539
809
|
*ngIf="!field.fields"
|
|
540
810
|
[fieldDescription]="field"
|
|
541
811
|
[value]="data[field.name]"
|
|
812
|
+
[options]="getResolvedOptions(field)"
|
|
813
|
+
[disabled]="getDisabled(field)"
|
|
814
|
+
[readOnly]="getReadOnly(field)"
|
|
815
|
+
[error]="getError(field)"
|
|
542
816
|
(onValueChangeField)="onFieldChange($event)"
|
|
817
|
+
(onBlurField)="handleBlurField($event)"
|
|
543
818
|
></dfk-field-input>
|
|
544
819
|
|
|
545
820
|
<div *ngIf="field.fields" [class]="field.className">
|
|
@@ -548,7 +823,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.20", ngImpo
|
|
|
548
823
|
*ngFor="
|
|
549
824
|
let item of getItems(field);
|
|
550
825
|
let i = index;
|
|
551
|
-
trackBy:
|
|
826
|
+
trackBy: groupTrackBy(field)
|
|
552
827
|
"
|
|
553
828
|
style="display: flex; align-items: flex-start; gap: 8px;"
|
|
554
829
|
>
|
|
@@ -556,11 +831,19 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.20", ngImpo
|
|
|
556
831
|
<dfk-multi-field-input
|
|
557
832
|
[fieldDescriptions]="field.fields"
|
|
558
833
|
[properties]="item"
|
|
834
|
+
[rootData]="rootData ?? data"
|
|
559
835
|
(onChange)="onGroupItemChange(field, i, $event)"
|
|
560
836
|
></dfk-multi-field-input>
|
|
561
837
|
</div>
|
|
562
838
|
<button
|
|
563
839
|
type="button"
|
|
840
|
+
[attr.aria-label]="
|
|
841
|
+
(field.removeLabel || 'Remove') +
|
|
842
|
+
' ' +
|
|
843
|
+
(field.label || field.name) +
|
|
844
|
+
' ' +
|
|
845
|
+
(i + 1)
|
|
846
|
+
"
|
|
564
847
|
(click)="onGroupItemRemove(field, i)"
|
|
565
848
|
[disabled]="!canRemoveItem(field)"
|
|
566
849
|
>
|
|
@@ -569,6 +852,9 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.20", ngImpo
|
|
|
569
852
|
</div>
|
|
570
853
|
<button
|
|
571
854
|
type="button"
|
|
855
|
+
[attr.aria-label]="
|
|
856
|
+
(field.addLabel || 'Add') + ' ' + (field.label || field.name)
|
|
857
|
+
"
|
|
572
858
|
(click)="onGroupItemAdd(field)"
|
|
573
859
|
[disabled]="!canAddItem(field)"
|
|
574
860
|
>
|
|
@@ -585,13 +871,419 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.20", ngImpo
|
|
|
585
871
|
type: Input
|
|
586
872
|
}], onChange: [{
|
|
587
873
|
type: Output
|
|
874
|
+
}], validityChange: [{
|
|
875
|
+
type: Output
|
|
876
|
+
}], onBlurField: [{
|
|
877
|
+
type: Output
|
|
588
878
|
}], layout: [{
|
|
589
879
|
type: Input
|
|
880
|
+
}], rootData: [{
|
|
881
|
+
type: Input
|
|
590
882
|
}], onWindowResize: [{
|
|
591
883
|
type: HostListener,
|
|
592
884
|
args: ['window:resize']
|
|
593
885
|
}] } });
|
|
594
886
|
|
|
887
|
+
class DynamicFormDevToolsComponent {
|
|
888
|
+
data = {};
|
|
889
|
+
errors = {};
|
|
890
|
+
touched = {};
|
|
891
|
+
isDirty = false;
|
|
892
|
+
fields = [];
|
|
893
|
+
/** Number of fields carrying errors, mirroring the react and vue overlays. */
|
|
894
|
+
errorCount() {
|
|
895
|
+
return Object.keys(this.errors || {}).length;
|
|
896
|
+
}
|
|
897
|
+
isOpen = signal(false);
|
|
898
|
+
activeTab = signal('data');
|
|
899
|
+
tabs = [
|
|
900
|
+
'data',
|
|
901
|
+
'errors',
|
|
902
|
+
'meta',
|
|
903
|
+
'fields',
|
|
904
|
+
];
|
|
905
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.25", ngImport: i0, type: DynamicFormDevToolsComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
906
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.25", type: DynamicFormDevToolsComponent, isStandalone: true, selector: "dfk-dev-tools", inputs: { data: "data", errors: "errors", touched: "touched", isDirty: "isDirty", fields: "fields" }, ngImport: i0, template: `
|
|
907
|
+
<button
|
|
908
|
+
*ngIf="!isOpen()"
|
|
909
|
+
type="button"
|
|
910
|
+
(click)="isOpen.set(true)"
|
|
911
|
+
style="
|
|
912
|
+
position: fixed;
|
|
913
|
+
right: 16px;
|
|
914
|
+
bottom: 16px;
|
|
915
|
+
z-index: 99999;
|
|
916
|
+
background: #1e293b;
|
|
917
|
+
color: #f8fafc;
|
|
918
|
+
border: 1px solid #334155;
|
|
919
|
+
border-radius: 20px;
|
|
920
|
+
padding: 8px 14px;
|
|
921
|
+
font-size: 12px;
|
|
922
|
+
font-weight: 600;
|
|
923
|
+
cursor: pointer;
|
|
924
|
+
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
|
|
925
|
+
display: flex;
|
|
926
|
+
align-items: center;
|
|
927
|
+
gap: 6px;
|
|
928
|
+
"
|
|
929
|
+
>
|
|
930
|
+
<span>🔍 DevTools</span>
|
|
931
|
+
<span
|
|
932
|
+
*ngIf="errorCount() > 0"
|
|
933
|
+
class="dfk-devtools-badge"
|
|
934
|
+
style="
|
|
935
|
+
background: #ef4444;
|
|
936
|
+
color: #fff;
|
|
937
|
+
border-radius: 10px;
|
|
938
|
+
padding: 2px 6px;
|
|
939
|
+
font-size: 10px;
|
|
940
|
+
"
|
|
941
|
+
>{{ errorCount() }}</span
|
|
942
|
+
>
|
|
943
|
+
</button>
|
|
944
|
+
|
|
945
|
+
<div
|
|
946
|
+
*ngIf="isOpen()"
|
|
947
|
+
style="
|
|
948
|
+
position: fixed;
|
|
949
|
+
right: 16px;
|
|
950
|
+
bottom: 16px;
|
|
951
|
+
z-index: 99999;
|
|
952
|
+
width: 360px;
|
|
953
|
+
max-height: 420px;
|
|
954
|
+
background: #0f172a;
|
|
955
|
+
color: #f8fafc;
|
|
956
|
+
border: 1px solid #334155;
|
|
957
|
+
border-radius: 12px;
|
|
958
|
+
box-shadow: 0 10px 25px rgba(0, 0, 0, 0.3);
|
|
959
|
+
display: flex;
|
|
960
|
+
flex-direction: column;
|
|
961
|
+
font-family: monospace, sans-serif;
|
|
962
|
+
font-size: 12px;
|
|
963
|
+
overflow: hidden;
|
|
964
|
+
"
|
|
965
|
+
>
|
|
966
|
+
<div
|
|
967
|
+
style="
|
|
968
|
+
padding: 10px 14px;
|
|
969
|
+
background: #1e293b;
|
|
970
|
+
display: flex;
|
|
971
|
+
justify-content: space-between;
|
|
972
|
+
align-items: center;
|
|
973
|
+
border-bottom: 1px solid #334155;
|
|
974
|
+
"
|
|
975
|
+
>
|
|
976
|
+
<span style="font-weight: bold; color: #38bdf8;">🛠️ Form DevTools</span>
|
|
977
|
+
<button
|
|
978
|
+
type="button"
|
|
979
|
+
(click)="isOpen.set(false)"
|
|
980
|
+
style="background: transparent; border: none; color: #94a3b8; font-size: 14px; cursor: pointer;"
|
|
981
|
+
>
|
|
982
|
+
✕
|
|
983
|
+
</button>
|
|
984
|
+
</div>
|
|
985
|
+
|
|
986
|
+
<div
|
|
987
|
+
style="display: flex; background: #1e293b; border-bottom: 1px solid #334155;"
|
|
988
|
+
>
|
|
989
|
+
<button
|
|
990
|
+
type="button"
|
|
991
|
+
*ngFor="let tab of tabs"
|
|
992
|
+
(click)="activeTab.set(tab)"
|
|
993
|
+
[style.background]="activeTab() === tab ? '#0f172a' : 'transparent'"
|
|
994
|
+
[style.color]="activeTab() === tab ? '#38bdf8' : '#94a3b8'"
|
|
995
|
+
style="flex: 1; padding: 6px 0; border: none; cursor: pointer; text-transform: capitalize; font-size: 11px;"
|
|
996
|
+
>
|
|
997
|
+
{{ tab
|
|
998
|
+
}}{{
|
|
999
|
+
tab === 'errors' && errorCount() > 0
|
|
1000
|
+
? ' (' + errorCount() + ')'
|
|
1001
|
+
: ''
|
|
1002
|
+
}}
|
|
1003
|
+
</button>
|
|
1004
|
+
</div>
|
|
1005
|
+
|
|
1006
|
+
<div style="padding: 12px; overflow-y: auto; flex: 1;">
|
|
1007
|
+
<pre
|
|
1008
|
+
*ngIf="activeTab() === 'data'"
|
|
1009
|
+
style="margin: 0; white-space: pre-wrap; word-break: break-all; color: #a7f3d0;"
|
|
1010
|
+
>
|
|
1011
|
+
{{ data | json }}
|
|
1012
|
+
</pre>
|
|
1013
|
+
<pre
|
|
1014
|
+
*ngIf="activeTab() === 'errors'"
|
|
1015
|
+
style="margin: 0; white-space: pre-wrap; word-break: break-all; color: #f87171;"
|
|
1016
|
+
>
|
|
1017
|
+
{{ errors | json }}
|
|
1018
|
+
</pre>
|
|
1019
|
+
<div *ngIf="activeTab() === 'meta'">
|
|
1020
|
+
<div>isDirty: {{ isDirty }}</div>
|
|
1021
|
+
<pre style="margin: 4px 0 0 0; color: #cbd5e1;">{{
|
|
1022
|
+
touched | json
|
|
1023
|
+
}}</pre>
|
|
1024
|
+
</div>
|
|
1025
|
+
<div *ngIf="activeTab() === 'fields'">
|
|
1026
|
+
<div
|
|
1027
|
+
*ngFor="let f of fields"
|
|
1028
|
+
style="padding: 6px; margin-bottom: 6px; background: #1e293b; border-radius: 4px;"
|
|
1029
|
+
>
|
|
1030
|
+
<div style="color: #38bdf8; font-weight: bold;">{{ f.name }}</div>
|
|
1031
|
+
<div style="color: #94a3b8; font-size: 10px;">
|
|
1032
|
+
type: {{ f.type }}
|
|
1033
|
+
</div>
|
|
1034
|
+
</div>
|
|
1035
|
+
</div>
|
|
1036
|
+
</div>
|
|
1037
|
+
</div>
|
|
1038
|
+
`, isInline: true, dependencies: [{ kind: "directive", type: NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: NgFor, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "pipe", type: JsonPipe, name: "json" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
1039
|
+
}
|
|
1040
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.25", ngImport: i0, type: DynamicFormDevToolsComponent, decorators: [{
|
|
1041
|
+
type: Component,
|
|
1042
|
+
args: [{
|
|
1043
|
+
selector: 'dfk-dev-tools',
|
|
1044
|
+
standalone: true,
|
|
1045
|
+
imports: [NgIf, NgFor, JsonPipe],
|
|
1046
|
+
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
1047
|
+
template: `
|
|
1048
|
+
<button
|
|
1049
|
+
*ngIf="!isOpen()"
|
|
1050
|
+
type="button"
|
|
1051
|
+
(click)="isOpen.set(true)"
|
|
1052
|
+
style="
|
|
1053
|
+
position: fixed;
|
|
1054
|
+
right: 16px;
|
|
1055
|
+
bottom: 16px;
|
|
1056
|
+
z-index: 99999;
|
|
1057
|
+
background: #1e293b;
|
|
1058
|
+
color: #f8fafc;
|
|
1059
|
+
border: 1px solid #334155;
|
|
1060
|
+
border-radius: 20px;
|
|
1061
|
+
padding: 8px 14px;
|
|
1062
|
+
font-size: 12px;
|
|
1063
|
+
font-weight: 600;
|
|
1064
|
+
cursor: pointer;
|
|
1065
|
+
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
|
|
1066
|
+
display: flex;
|
|
1067
|
+
align-items: center;
|
|
1068
|
+
gap: 6px;
|
|
1069
|
+
"
|
|
1070
|
+
>
|
|
1071
|
+
<span>🔍 DevTools</span>
|
|
1072
|
+
<span
|
|
1073
|
+
*ngIf="errorCount() > 0"
|
|
1074
|
+
class="dfk-devtools-badge"
|
|
1075
|
+
style="
|
|
1076
|
+
background: #ef4444;
|
|
1077
|
+
color: #fff;
|
|
1078
|
+
border-radius: 10px;
|
|
1079
|
+
padding: 2px 6px;
|
|
1080
|
+
font-size: 10px;
|
|
1081
|
+
"
|
|
1082
|
+
>{{ errorCount() }}</span
|
|
1083
|
+
>
|
|
1084
|
+
</button>
|
|
1085
|
+
|
|
1086
|
+
<div
|
|
1087
|
+
*ngIf="isOpen()"
|
|
1088
|
+
style="
|
|
1089
|
+
position: fixed;
|
|
1090
|
+
right: 16px;
|
|
1091
|
+
bottom: 16px;
|
|
1092
|
+
z-index: 99999;
|
|
1093
|
+
width: 360px;
|
|
1094
|
+
max-height: 420px;
|
|
1095
|
+
background: #0f172a;
|
|
1096
|
+
color: #f8fafc;
|
|
1097
|
+
border: 1px solid #334155;
|
|
1098
|
+
border-radius: 12px;
|
|
1099
|
+
box-shadow: 0 10px 25px rgba(0, 0, 0, 0.3);
|
|
1100
|
+
display: flex;
|
|
1101
|
+
flex-direction: column;
|
|
1102
|
+
font-family: monospace, sans-serif;
|
|
1103
|
+
font-size: 12px;
|
|
1104
|
+
overflow: hidden;
|
|
1105
|
+
"
|
|
1106
|
+
>
|
|
1107
|
+
<div
|
|
1108
|
+
style="
|
|
1109
|
+
padding: 10px 14px;
|
|
1110
|
+
background: #1e293b;
|
|
1111
|
+
display: flex;
|
|
1112
|
+
justify-content: space-between;
|
|
1113
|
+
align-items: center;
|
|
1114
|
+
border-bottom: 1px solid #334155;
|
|
1115
|
+
"
|
|
1116
|
+
>
|
|
1117
|
+
<span style="font-weight: bold; color: #38bdf8;">🛠️ Form DevTools</span>
|
|
1118
|
+
<button
|
|
1119
|
+
type="button"
|
|
1120
|
+
(click)="isOpen.set(false)"
|
|
1121
|
+
style="background: transparent; border: none; color: #94a3b8; font-size: 14px; cursor: pointer;"
|
|
1122
|
+
>
|
|
1123
|
+
✕
|
|
1124
|
+
</button>
|
|
1125
|
+
</div>
|
|
1126
|
+
|
|
1127
|
+
<div
|
|
1128
|
+
style="display: flex; background: #1e293b; border-bottom: 1px solid #334155;"
|
|
1129
|
+
>
|
|
1130
|
+
<button
|
|
1131
|
+
type="button"
|
|
1132
|
+
*ngFor="let tab of tabs"
|
|
1133
|
+
(click)="activeTab.set(tab)"
|
|
1134
|
+
[style.background]="activeTab() === tab ? '#0f172a' : 'transparent'"
|
|
1135
|
+
[style.color]="activeTab() === tab ? '#38bdf8' : '#94a3b8'"
|
|
1136
|
+
style="flex: 1; padding: 6px 0; border: none; cursor: pointer; text-transform: capitalize; font-size: 11px;"
|
|
1137
|
+
>
|
|
1138
|
+
{{ tab
|
|
1139
|
+
}}{{
|
|
1140
|
+
tab === 'errors' && errorCount() > 0
|
|
1141
|
+
? ' (' + errorCount() + ')'
|
|
1142
|
+
: ''
|
|
1143
|
+
}}
|
|
1144
|
+
</button>
|
|
1145
|
+
</div>
|
|
1146
|
+
|
|
1147
|
+
<div style="padding: 12px; overflow-y: auto; flex: 1;">
|
|
1148
|
+
<pre
|
|
1149
|
+
*ngIf="activeTab() === 'data'"
|
|
1150
|
+
style="margin: 0; white-space: pre-wrap; word-break: break-all; color: #a7f3d0;"
|
|
1151
|
+
>
|
|
1152
|
+
{{ data | json }}
|
|
1153
|
+
</pre>
|
|
1154
|
+
<pre
|
|
1155
|
+
*ngIf="activeTab() === 'errors'"
|
|
1156
|
+
style="margin: 0; white-space: pre-wrap; word-break: break-all; color: #f87171;"
|
|
1157
|
+
>
|
|
1158
|
+
{{ errors | json }}
|
|
1159
|
+
</pre>
|
|
1160
|
+
<div *ngIf="activeTab() === 'meta'">
|
|
1161
|
+
<div>isDirty: {{ isDirty }}</div>
|
|
1162
|
+
<pre style="margin: 4px 0 0 0; color: #cbd5e1;">{{
|
|
1163
|
+
touched | json
|
|
1164
|
+
}}</pre>
|
|
1165
|
+
</div>
|
|
1166
|
+
<div *ngIf="activeTab() === 'fields'">
|
|
1167
|
+
<div
|
|
1168
|
+
*ngFor="let f of fields"
|
|
1169
|
+
style="padding: 6px; margin-bottom: 6px; background: #1e293b; border-radius: 4px;"
|
|
1170
|
+
>
|
|
1171
|
+
<div style="color: #38bdf8; font-weight: bold;">{{ f.name }}</div>
|
|
1172
|
+
<div style="color: #94a3b8; font-size: 10px;">
|
|
1173
|
+
type: {{ f.type }}
|
|
1174
|
+
</div>
|
|
1175
|
+
</div>
|
|
1176
|
+
</div>
|
|
1177
|
+
</div>
|
|
1178
|
+
</div>
|
|
1179
|
+
`,
|
|
1180
|
+
}]
|
|
1181
|
+
}], propDecorators: { data: [{
|
|
1182
|
+
type: Input
|
|
1183
|
+
}], errors: [{
|
|
1184
|
+
type: Input
|
|
1185
|
+
}], touched: [{
|
|
1186
|
+
type: Input
|
|
1187
|
+
}], isDirty: [{
|
|
1188
|
+
type: Input
|
|
1189
|
+
}], fields: [{
|
|
1190
|
+
type: Input
|
|
1191
|
+
}] } });
|
|
1192
|
+
|
|
1193
|
+
function createDynamicFormStore(options) {
|
|
1194
|
+
const fields = options.fields;
|
|
1195
|
+
const initialValues = options.initialValues || {};
|
|
1196
|
+
const validateOnBlur = options.validateOnBlur ?? true;
|
|
1197
|
+
const validateOnChange = options.validateOnChange ?? false;
|
|
1198
|
+
const data = signal(applyComputedValues(fields, initialValues));
|
|
1199
|
+
const errors = signal({});
|
|
1200
|
+
const isDirty = signal(false);
|
|
1201
|
+
const touched = signal({});
|
|
1202
|
+
const isSubmitting = signal(false);
|
|
1203
|
+
const isSubmitted = signal(false);
|
|
1204
|
+
const isValid = computed(() => Object.keys(errors()).length === 0);
|
|
1205
|
+
function validate() {
|
|
1206
|
+
const res = validateFields(fields, data());
|
|
1207
|
+
errors.set(res.errors);
|
|
1208
|
+
return res.valid;
|
|
1209
|
+
}
|
|
1210
|
+
function handleChange(newData) {
|
|
1211
|
+
const next = applyComputedValues(fields, newData);
|
|
1212
|
+
data.set(next);
|
|
1213
|
+
isDirty.set(true);
|
|
1214
|
+
if (validateOnChange) {
|
|
1215
|
+
const res = validateFields(fields, next);
|
|
1216
|
+
errors.set(res.errors);
|
|
1217
|
+
}
|
|
1218
|
+
}
|
|
1219
|
+
function setFieldValue(name, value) {
|
|
1220
|
+
handleChange({ ...data(), [name]: value });
|
|
1221
|
+
}
|
|
1222
|
+
function setFieldTouched(name, isTouched = true) {
|
|
1223
|
+
touched.set({ ...touched(), [name]: isTouched });
|
|
1224
|
+
}
|
|
1225
|
+
function handleBlur(fieldName) {
|
|
1226
|
+
setFieldTouched(fieldName, true);
|
|
1227
|
+
if (validateOnBlur) {
|
|
1228
|
+
const res = validateFields(fields, data());
|
|
1229
|
+
errors.set(res.errors);
|
|
1230
|
+
}
|
|
1231
|
+
}
|
|
1232
|
+
function reset(newValues) {
|
|
1233
|
+
const seed = newValues ?? initialValues;
|
|
1234
|
+
const next = applyComputedValues(fields, seed);
|
|
1235
|
+
data.set(next);
|
|
1236
|
+
errors.set({});
|
|
1237
|
+
isDirty.set(false);
|
|
1238
|
+
touched.set({});
|
|
1239
|
+
isSubmitting.set(false);
|
|
1240
|
+
isSubmitted.set(false);
|
|
1241
|
+
}
|
|
1242
|
+
/**
|
|
1243
|
+
* Returns a submit handler, mirroring the React and Vue `useDynamicForm`
|
|
1244
|
+
* hooks. Bind it once and use it as the `(ngSubmit)` handler:
|
|
1245
|
+
* `onSubmit = this.store.handleSubmit(data => ...)`.
|
|
1246
|
+
*/
|
|
1247
|
+
function handleSubmit(onValid, onInvalid) {
|
|
1248
|
+
return async (e) => {
|
|
1249
|
+
if (e && typeof e.preventDefault === 'function') {
|
|
1250
|
+
e.preventDefault();
|
|
1251
|
+
}
|
|
1252
|
+
isSubmitting.set(true);
|
|
1253
|
+
try {
|
|
1254
|
+
const res = validateFields(fields, data());
|
|
1255
|
+
errors.set(res.errors);
|
|
1256
|
+
isSubmitted.set(true);
|
|
1257
|
+
if (res.valid) {
|
|
1258
|
+
await onValid(data());
|
|
1259
|
+
}
|
|
1260
|
+
else if (onInvalid) {
|
|
1261
|
+
onInvalid(res.errors);
|
|
1262
|
+
}
|
|
1263
|
+
}
|
|
1264
|
+
finally {
|
|
1265
|
+
isSubmitting.set(false);
|
|
1266
|
+
}
|
|
1267
|
+
};
|
|
1268
|
+
}
|
|
1269
|
+
return {
|
|
1270
|
+
data,
|
|
1271
|
+
errors,
|
|
1272
|
+
isValid,
|
|
1273
|
+
isDirty,
|
|
1274
|
+
touched,
|
|
1275
|
+
isSubmitting,
|
|
1276
|
+
isSubmitted,
|
|
1277
|
+
setFieldValue,
|
|
1278
|
+
setFieldTouched,
|
|
1279
|
+
handleChange,
|
|
1280
|
+
handleBlur,
|
|
1281
|
+
reset,
|
|
1282
|
+
validate,
|
|
1283
|
+
handleSubmit,
|
|
1284
|
+
};
|
|
1285
|
+
}
|
|
1286
|
+
|
|
595
1287
|
class LayoutRegistry {
|
|
596
1288
|
layouts = new Map();
|
|
597
1289
|
register(type, component) {
|
|
@@ -612,8 +1304,8 @@ class ColumnLayout {
|
|
|
612
1304
|
get gap() {
|
|
613
1305
|
return this.config?.gap ?? 12;
|
|
614
1306
|
}
|
|
615
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.
|
|
616
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.
|
|
1307
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.25", ngImport: i0, type: ColumnLayout, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
1308
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.25", type: ColumnLayout, isStandalone: true, selector: "dfk-column-layout", inputs: { config: "config", template: "template" }, ngImport: i0, template: `<div
|
|
617
1309
|
[style.display]="'flex'"
|
|
618
1310
|
[style.flexDirection]="'column'"
|
|
619
1311
|
[style.gap]="gap + 'px'"
|
|
@@ -621,7 +1313,7 @@ class ColumnLayout {
|
|
|
621
1313
|
<ng-container *ngTemplateOutlet="template"></ng-container>
|
|
622
1314
|
</div>`, isInline: true, dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }] });
|
|
623
1315
|
}
|
|
624
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.
|
|
1316
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.25", ngImport: i0, type: ColumnLayout, decorators: [{
|
|
625
1317
|
type: Component,
|
|
626
1318
|
args: [{
|
|
627
1319
|
selector: 'dfk-column-layout',
|
|
@@ -646,8 +1338,8 @@ class RowLayout {
|
|
|
646
1338
|
get gap() {
|
|
647
1339
|
return this.config?.gap ?? 12;
|
|
648
1340
|
}
|
|
649
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.
|
|
650
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.
|
|
1341
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.25", ngImport: i0, type: RowLayout, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
1342
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.25", type: RowLayout, isStandalone: true, selector: "dfk-row-layout", inputs: { config: "config", template: "template" }, ngImport: i0, template: `<div
|
|
651
1343
|
[style.display]="'flex'"
|
|
652
1344
|
[style.flexDirection]="'row'"
|
|
653
1345
|
[style.gap]="gap + 'px'"
|
|
@@ -655,7 +1347,7 @@ class RowLayout {
|
|
|
655
1347
|
<ng-container *ngTemplateOutlet="template"></ng-container>
|
|
656
1348
|
</div>`, isInline: true, dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }] });
|
|
657
1349
|
}
|
|
658
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.
|
|
1350
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.25", ngImport: i0, type: RowLayout, decorators: [{
|
|
659
1351
|
type: Component,
|
|
660
1352
|
args: [{
|
|
661
1353
|
selector: 'dfk-row-layout',
|
|
@@ -683,8 +1375,8 @@ class GridLayout {
|
|
|
683
1375
|
get gap() {
|
|
684
1376
|
return this.config?.gap ?? 12;
|
|
685
1377
|
}
|
|
686
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.
|
|
687
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.
|
|
1378
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.25", ngImport: i0, type: GridLayout, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
1379
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.25", type: GridLayout, isStandalone: true, selector: "dfk-grid-layout", inputs: { config: "config", template: "template" }, ngImport: i0, template: `<div
|
|
688
1380
|
[style.display]="'grid'"
|
|
689
1381
|
[style.gridTemplateColumns]="'repeat(' + columns + ', 1fr)'"
|
|
690
1382
|
[style.gap]="gap + 'px'"
|
|
@@ -692,7 +1384,7 @@ class GridLayout {
|
|
|
692
1384
|
<ng-container *ngTemplateOutlet="template"></ng-container>
|
|
693
1385
|
</div>`, isInline: true, dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }] });
|
|
694
1386
|
}
|
|
695
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.
|
|
1387
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.25", ngImport: i0, type: GridLayout, decorators: [{
|
|
696
1388
|
type: Component,
|
|
697
1389
|
args: [{
|
|
698
1390
|
selector: 'dfk-grid-layout',
|
|
@@ -716,8 +1408,8 @@ layoutRegistry.register('row', RowLayout);
|
|
|
716
1408
|
layoutRegistry.register('grid', GridLayout);
|
|
717
1409
|
|
|
718
1410
|
class DynamicFieldKitModule {
|
|
719
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.
|
|
720
|
-
static ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "19.2.
|
|
1411
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.25", ngImport: i0, type: DynamicFieldKitModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule });
|
|
1412
|
+
static ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "19.2.25", ngImport: i0, type: DynamicFieldKitModule, imports: [CommonModule,
|
|
721
1413
|
DynamicInput,
|
|
722
1414
|
FieldInput,
|
|
723
1415
|
MultiFieldInput,
|
|
@@ -729,7 +1421,7 @@ class DynamicFieldKitModule {
|
|
|
729
1421
|
ColumnLayout,
|
|
730
1422
|
RowLayout,
|
|
731
1423
|
GridLayout] });
|
|
732
|
-
static ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "19.2.
|
|
1424
|
+
static ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "19.2.25", ngImport: i0, type: DynamicFieldKitModule, imports: [CommonModule,
|
|
733
1425
|
DynamicInput,
|
|
734
1426
|
FieldInput,
|
|
735
1427
|
MultiFieldInput,
|
|
@@ -737,7 +1429,7 @@ class DynamicFieldKitModule {
|
|
|
737
1429
|
RowLayout,
|
|
738
1430
|
GridLayout] });
|
|
739
1431
|
}
|
|
740
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.
|
|
1432
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.25", ngImport: i0, type: DynamicFieldKitModule, decorators: [{
|
|
741
1433
|
type: NgModule,
|
|
742
1434
|
args: [{
|
|
743
1435
|
imports: [
|
|
@@ -767,5 +1459,4 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.20", ngImpo
|
|
|
767
1459
|
* Generated bundle index. Do not edit.
|
|
768
1460
|
*/
|
|
769
1461
|
|
|
770
|
-
export { BaseInputComponent, ColumnLayout, DynamicFieldKitModule, DynamicInput, FieldInput, GridLayout, LayoutRegistry, MultiFieldInput, RowLayout, layoutRegistry };
|
|
771
|
-
//# sourceMappingURL=dynamic-field-kit-angular.mjs.map
|
|
1462
|
+
export { BaseInputComponent, ColumnLayout, DynamicFieldKitModule, DynamicFormDevToolsComponent, DynamicInput, FIELD_REGISTRY, FieldInput, GridLayout, LayoutRegistry, MultiFieldInput, RowLayout, createDynamicFormStore, layoutRegistry };
|