@ng-forge/dynamic-forms 0.8.0-next.4 → 0.8.0-next.5
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/fesm2022/{ng-forge-dynamic-forms-array-field.component-CJ_OclbR.mjs → ng-forge-dynamic-forms-array-field.component-DWhbvZUO.mjs} +3 -3
- package/fesm2022/{ng-forge-dynamic-forms-array-field.component-CJ_OclbR.mjs.map → ng-forge-dynamic-forms-array-field.component-DWhbvZUO.mjs.map} +1 -1
- package/fesm2022/{ng-forge-dynamic-forms-container-field.component-DQ_OQLtx.mjs → ng-forge-dynamic-forms-container-field.component-ByKgY4-b.mjs} +4 -9
- package/fesm2022/ng-forge-dynamic-forms-container-field.component-ByKgY4-b.mjs.map +1 -0
- package/fesm2022/{ng-forge-dynamic-forms-container-utils-zQSTe7Jk.mjs → ng-forge-dynamic-forms-container-utils-DXRcEEkE.mjs} +4 -5
- package/fesm2022/ng-forge-dynamic-forms-container-utils-DXRcEEkE.mjs.map +1 -0
- package/fesm2022/{ng-forge-dynamic-forms-css-wrapper.component-2fdmbtL7.mjs → ng-forge-dynamic-forms-css-wrapper.component-ClFdxZV4.mjs} +2 -2
- package/fesm2022/{ng-forge-dynamic-forms-css-wrapper.component-2fdmbtL7.mjs.map → ng-forge-dynamic-forms-css-wrapper.component-ClFdxZV4.mjs.map} +1 -1
- package/fesm2022/{ng-forge-dynamic-forms-emit-initialization-pCNwBRRm.mjs → ng-forge-dynamic-forms-emit-initialization-CTpgJ-02.mjs} +3 -3
- package/fesm2022/ng-forge-dynamic-forms-emit-initialization-CTpgJ-02.mjs.map +1 -0
- package/fesm2022/{ng-forge-dynamic-forms-group-field.component-BdLdSEHj.mjs → ng-forge-dynamic-forms-group-field.component-clrQsUyY.mjs} +3 -3
- package/fesm2022/{ng-forge-dynamic-forms-group-field.component-BdLdSEHj.mjs.map → ng-forge-dynamic-forms-group-field.component-clrQsUyY.mjs.map} +1 -1
- package/fesm2022/ng-forge-dynamic-forms-integration.mjs +3 -4
- package/fesm2022/ng-forge-dynamic-forms-integration.mjs.map +1 -1
- package/fesm2022/{ng-forge-dynamic-forms-ng-forge-dynamic-forms-BdMxsqmk.mjs → ng-forge-dynamic-forms-ng-forge-dynamic-forms-Bw9Bt0Ev.mjs} +492 -394
- package/fesm2022/ng-forge-dynamic-forms-ng-forge-dynamic-forms-Bw9Bt0Ev.mjs.map +1 -0
- package/fesm2022/{ng-forge-dynamic-forms-page-field.component-C5rUo80O.mjs → ng-forge-dynamic-forms-page-field.component-XtyjFlJi.mjs} +3 -3
- package/fesm2022/{ng-forge-dynamic-forms-page-field.component-C5rUo80O.mjs.map → ng-forge-dynamic-forms-page-field.component-XtyjFlJi.mjs.map} +1 -1
- package/fesm2022/{ng-forge-dynamic-forms-text-field.component-lvuyTM1r.mjs → ng-forge-dynamic-forms-text-field.component-CJsRWH2r.mjs} +2 -2
- package/fesm2022/{ng-forge-dynamic-forms-text-field.component-lvuyTM1r.mjs.map → ng-forge-dynamic-forms-text-field.component-CJsRWH2r.mjs.map} +1 -1
- package/fesm2022/ng-forge-dynamic-forms.mjs +1 -1
- package/package.json +1 -1
- package/types/ng-forge-dynamic-forms-integration.d.ts +3 -4
- package/types/ng-forge-dynamic-forms.d.ts +68 -27
- package/fesm2022/ng-forge-dynamic-forms-container-field.component-DQ_OQLtx.mjs.map +0 -1
- package/fesm2022/ng-forge-dynamic-forms-container-utils-zQSTe7Jk.mjs.map +0 -1
- package/fesm2022/ng-forge-dynamic-forms-emit-initialization-pCNwBRRm.mjs.map +0 -1
- package/fesm2022/ng-forge-dynamic-forms-ng-forge-dynamic-forms-BdMxsqmk.mjs.map +0 -1
|
@@ -1318,13 +1318,11 @@ function setupInitializationTracking(options) {
|
|
|
1318
1318
|
}
|
|
1319
1319
|
|
|
1320
1320
|
/**
|
|
1321
|
-
* Type guard
|
|
1322
|
-
*
|
|
1323
|
-
* @param field - The field to check
|
|
1324
|
-
* @returns True if the field is a HiddenField
|
|
1321
|
+
* Type guard for RowField with proper type narrowing
|
|
1325
1322
|
*/
|
|
1326
|
-
|
|
1327
|
-
|
|
1323
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Type guard must accept any field type
|
|
1324
|
+
function isRowField(field) {
|
|
1325
|
+
return field.type === 'row' && 'fields' in field && Array.isArray(field.fields);
|
|
1328
1326
|
}
|
|
1329
1327
|
|
|
1330
1328
|
/**
|
|
@@ -1336,41 +1334,20 @@ function isGroupField(field) {
|
|
|
1336
1334
|
}
|
|
1337
1335
|
|
|
1338
1336
|
/**
|
|
1339
|
-
* Type guard for
|
|
1340
|
-
*/
|
|
1341
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Type guard must accept any field type
|
|
1342
|
-
function isRowField(field) {
|
|
1343
|
-
return field.type === 'row' && 'fields' in field && Array.isArray(field.fields);
|
|
1344
|
-
}
|
|
1345
|
-
/**
|
|
1346
|
-
* Validates that a row field doesn't contain hidden fields.
|
|
1347
|
-
* Hidden fields are not allowed in rows because rows are for horizontal layouts,
|
|
1348
|
-
* and hidden fields don't render anything.
|
|
1337
|
+
* Type guard for ContainerField with proper type narrowing.
|
|
1349
1338
|
*
|
|
1350
|
-
*
|
|
1351
|
-
*
|
|
1352
|
-
|
|
1353
|
-
|
|
1354
|
-
return !hasHiddenFields(rowField.fields);
|
|
1355
|
-
}
|
|
1356
|
-
/**
|
|
1357
|
-
* Recursively checks if fields contain any hidden fields
|
|
1358
|
-
* @param fields Array of field definitions to check
|
|
1359
|
-
* @returns true if hidden fields found, false otherwise
|
|
1339
|
+
* `wrappers` is part of the type but optional in practice — many configs use
|
|
1340
|
+
* containers purely as flex/layout wrappers without any wrapper components.
|
|
1341
|
+
* Match on `type === 'container'` and the `fields` shape only, mirroring
|
|
1342
|
+
* `isPageField` / `isRowField`.
|
|
1360
1343
|
*/
|
|
1361
|
-
|
|
1362
|
-
|
|
1363
|
-
|
|
1364
|
-
|
|
1365
|
-
|
|
1366
|
-
|
|
1367
|
-
|
|
1368
|
-
if (hasHiddenFields(field.fields)) {
|
|
1369
|
-
return true;
|
|
1370
|
-
}
|
|
1371
|
-
}
|
|
1372
|
-
}
|
|
1373
|
-
return false;
|
|
1344
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Type guard must accept any field type
|
|
1345
|
+
function isContainerTypedField(field) {
|
|
1346
|
+
return (field.type === 'container' &&
|
|
1347
|
+
'wrappers' in field &&
|
|
1348
|
+
'fields' in field &&
|
|
1349
|
+
Array.isArray(field.wrappers) &&
|
|
1350
|
+
Array.isArray(field.fields));
|
|
1374
1351
|
}
|
|
1375
1352
|
|
|
1376
1353
|
/**
|
|
@@ -1391,7 +1368,9 @@ function validatePageNesting(pageField) {
|
|
|
1391
1368
|
* Type guard to check if a field is a container with fields property
|
|
1392
1369
|
*/
|
|
1393
1370
|
function isContainerWithFields$1(field) {
|
|
1394
|
-
return (isRowField(field) || isGroupField(field)
|
|
1371
|
+
return ((isRowField(field) || isGroupField(field) || isContainerTypedField(field)) &&
|
|
1372
|
+
'fields' in field &&
|
|
1373
|
+
Array.isArray(field.fields));
|
|
1395
1374
|
}
|
|
1396
1375
|
/**
|
|
1397
1376
|
* Recursively checks if fields contain any nested page fields
|
|
@@ -1431,14 +1410,6 @@ function isSimplifiedArrayField(field) {
|
|
|
1431
1410
|
return field.type === 'array' && 'template' in field;
|
|
1432
1411
|
}
|
|
1433
1412
|
|
|
1434
|
-
/**
|
|
1435
|
-
* Type guard for ContainerField with proper type narrowing
|
|
1436
|
-
*/
|
|
1437
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Type guard must accept any field type
|
|
1438
|
-
function isContainerTypedField(field) {
|
|
1439
|
-
return field.type === 'container' && 'fields' in field && 'wrappers' in field;
|
|
1440
|
-
}
|
|
1441
|
-
|
|
1442
1413
|
/**
|
|
1443
1414
|
* Type guard to check if a field definition has child fields.
|
|
1444
1415
|
* This is a looser check that works with FieldDef<unknown> without requiring RegisteredFieldTypes.
|
|
@@ -4170,9 +4141,9 @@ class PageOrchestratorComponent {
|
|
|
4170
4141
|
}
|
|
4171
4142
|
}
|
|
4172
4143
|
}
|
|
4173
|
-
`, isInline: true, styles: [":host{display:block;position:relative;width:100%}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush, deferBlockDependencies: [() => [import('./ng-forge-dynamic-forms-page-field.component-
|
|
4144
|
+
`, isInline: true, styles: [":host{display:block;position:relative;width:100%}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush, deferBlockDependencies: [() => [import('./ng-forge-dynamic-forms-page-field.component-XtyjFlJi.mjs').then(m => m.default)], () => [import('./ng-forge-dynamic-forms-page-field.component-XtyjFlJi.mjs').then(m => m.default)]] });
|
|
4174
4145
|
}
|
|
4175
|
-
i0.ɵɵngDeclareClassMetadataAsync({ minVersion: "18.0.0", version: "21.2.8", ngImport: i0, type: PageOrchestratorComponent, resolveDeferredDeps: () => [import('./ng-forge-dynamic-forms-page-field.component-
|
|
4146
|
+
i0.ɵɵngDeclareClassMetadataAsync({ minVersion: "18.0.0", version: "21.2.8", ngImport: i0, type: PageOrchestratorComponent, resolveDeferredDeps: () => [import('./ng-forge-dynamic-forms-page-field.component-XtyjFlJi.mjs').then(m => m.default)], resolveMetadata: PageFieldComponent => ({ decorators: [{
|
|
4176
4147
|
type: Component,
|
|
4177
4148
|
args: [{ selector: 'div[page-orchestrator]', imports: [PageFieldComponent], template: `
|
|
4178
4149
|
@for (pageField of pageFields(); track pageField.key; let i = $index) {
|
|
@@ -4558,7 +4529,7 @@ function hasAnyPageFields(fields) {
|
|
|
4558
4529
|
return true;
|
|
4559
4530
|
}
|
|
4560
4531
|
// Check nested fields in container types
|
|
4561
|
-
if (
|
|
4532
|
+
if (!isPageField(field) && isContainerWithFields(field)) {
|
|
4562
4533
|
if (hasAnyPageFields(field.fields)) {
|
|
4563
4534
|
return true;
|
|
4564
4535
|
}
|
|
@@ -4581,7 +4552,7 @@ function hasNestedPageFields(fields) {
|
|
|
4581
4552
|
}
|
|
4582
4553
|
}
|
|
4583
4554
|
// Check other container types for nested pages
|
|
4584
|
-
if (
|
|
4555
|
+
if (!isPageField(field) && isContainerWithFields(field)) {
|
|
4585
4556
|
if (hasNestedPageFields(field.fields)) {
|
|
4586
4557
|
return true;
|
|
4587
4558
|
}
|
|
@@ -6318,12 +6289,22 @@ function mapFieldToForm(fieldDef, fieldPath) {
|
|
|
6318
6289
|
}
|
|
6319
6290
|
/**
|
|
6320
6291
|
* Maps children of a container field (page, row, group) to the form schema.
|
|
6292
|
+
*
|
|
6293
|
+
* Layout containers (page/row/container) flatten into `parentPath` and are
|
|
6294
|
+
* recursed through without consuming a key — same contract as `mapFieldToForm`'s
|
|
6295
|
+
* top-level layout branch, so any depth of layout-container nesting reaches its
|
|
6296
|
+
* leaf descendants. Group/array fields consume their key and look up their own
|
|
6297
|
+
* child path on `parentPath`.
|
|
6321
6298
|
*/
|
|
6322
6299
|
function mapContainerChildren(fields, parentPath) {
|
|
6323
6300
|
if (!fields)
|
|
6324
6301
|
return;
|
|
6325
6302
|
const pathRecord = parentPath;
|
|
6326
6303
|
for (const field of fields) {
|
|
6304
|
+
if (isPageField(field) || isRowField(field) || isContainerTypedField(field)) {
|
|
6305
|
+
mapContainerChildren(field.fields, parentPath);
|
|
6306
|
+
continue;
|
|
6307
|
+
}
|
|
6327
6308
|
if (!field.key)
|
|
6328
6309
|
continue;
|
|
6329
6310
|
const childPath = pathRecord[field.key];
|
|
@@ -6419,9 +6400,21 @@ function mapArrayFieldToForm(arrayField, fieldPath) {
|
|
|
6419
6400
|
maxLength(fieldPath, arrayField.maxLength);
|
|
6420
6401
|
}
|
|
6421
6402
|
// Fields can be either FieldDef (primitive) or FieldDef[] (object)
|
|
6422
|
-
|
|
6423
|
-
//
|
|
6424
|
-
//
|
|
6403
|
+
let itemDefinitions = arrayField.fields;
|
|
6404
|
+
// Simplified arrays initialized without `value` have empty `fields`; their
|
|
6405
|
+
// item shape only lives in Symbol metadata. Fall back to that template so
|
|
6406
|
+
// validators and logic declared inside it still apply when items are
|
|
6407
|
+
// materialized from a programmatically-set form value.
|
|
6408
|
+
if (!itemDefinitions || itemDefinitions.length === 0) {
|
|
6409
|
+
const metadataTemplate = getNormalizedArrayMetadata(arrayField)?.template;
|
|
6410
|
+
if (metadataTemplate) {
|
|
6411
|
+
itemDefinitions = [
|
|
6412
|
+
Array.isArray(metadataTemplate) ? [...metadataTemplate] : metadataTemplate,
|
|
6413
|
+
];
|
|
6414
|
+
}
|
|
6415
|
+
}
|
|
6416
|
+
// Empty array with no template metadata - items will be added via buttons with
|
|
6417
|
+
// their own templates. Create a minimal schema that just accepts any value.
|
|
6425
6418
|
if (!itemDefinitions || itemDefinitions.length === 0) {
|
|
6426
6419
|
const emptyItemSchema = schema(() => {
|
|
6427
6420
|
// No fields to map - items will be added dynamically via buttons
|
|
@@ -6433,14 +6426,22 @@ function mapArrayFieldToForm(arrayField, fieldPath) {
|
|
|
6433
6426
|
let hasPrimitiveItems = false;
|
|
6434
6427
|
const allObjectFields = [];
|
|
6435
6428
|
for (const itemDef of itemDefinitions) {
|
|
6436
|
-
if (
|
|
6437
|
-
// Primitive item: single FieldDef
|
|
6438
|
-
hasPrimitiveItems = true;
|
|
6439
|
-
}
|
|
6440
|
-
else {
|
|
6429
|
+
if (Array.isArray(itemDef)) {
|
|
6441
6430
|
// Object item: collect fields for superset schema
|
|
6442
6431
|
collectFieldsFromObjectItem(itemDef, allObjectFields);
|
|
6443
6432
|
}
|
|
6433
|
+
else if (isPageField(itemDef) ||
|
|
6434
|
+
isRowField(itemDef) ||
|
|
6435
|
+
isContainerTypedField(itemDef)) {
|
|
6436
|
+
// Layout container as a single-template item: its descendants describe
|
|
6437
|
+
// the item's object shape. Hand it to the object-item collector wrapped
|
|
6438
|
+
// as a 1-element array so the structured-item mapping path runs.
|
|
6439
|
+
collectFieldsFromObjectItem([itemDef], allObjectFields);
|
|
6440
|
+
}
|
|
6441
|
+
else {
|
|
6442
|
+
// Primitive item: single FieldDef
|
|
6443
|
+
hasPrimitiveItems = true;
|
|
6444
|
+
}
|
|
6444
6445
|
}
|
|
6445
6446
|
// For pure primitive arrays, use a simple schema
|
|
6446
6447
|
if (hasPrimitiveItems && allObjectFields.length === 0) {
|
|
@@ -6580,7 +6581,7 @@ function getFieldDefaultValue(field, registry) {
|
|
|
6580
6581
|
if (valueHandling === 'exclude') {
|
|
6581
6582
|
return undefined;
|
|
6582
6583
|
}
|
|
6583
|
-
// Flatten fields (row/page) return flattened object of children's values
|
|
6584
|
+
// Flatten fields (row/page/container) return flattened object of children's values
|
|
6584
6585
|
// This is used by array fields to get the template structure
|
|
6585
6586
|
// Note: fieldsToDefaultValues will skip these at top-level since they have keys and return objects
|
|
6586
6587
|
if (valueHandling === 'flatten' && 'fields' in field && field.fields) {
|
|
@@ -6588,11 +6589,18 @@ function getFieldDefaultValue(field, registry) {
|
|
|
6588
6589
|
// Collect only fields that contribute values (exclude buttons, text, etc.)
|
|
6589
6590
|
const flattenedValues = {};
|
|
6590
6591
|
for (const childField of childFields) {
|
|
6591
|
-
|
|
6592
|
-
|
|
6593
|
-
|
|
6594
|
-
|
|
6595
|
-
|
|
6592
|
+
const childValueHandling = getFieldValueHandling(childField.type, registry);
|
|
6593
|
+
if (childValueHandling === 'exclude')
|
|
6594
|
+
continue;
|
|
6595
|
+
const childValue = getFieldDefaultValue(childField, registry);
|
|
6596
|
+
if (childValue === undefined)
|
|
6597
|
+
continue;
|
|
6598
|
+
if (childValueHandling === 'flatten' && childValue && typeof childValue === 'object' && !Array.isArray(childValue)) {
|
|
6599
|
+
// Nested flatten field — its value is already flat; spread it up.
|
|
6600
|
+
Object.assign(flattenedValues, childValue);
|
|
6601
|
+
}
|
|
6602
|
+
else if ('key' in childField && childField.key) {
|
|
6603
|
+
flattenedValues[childField.key] = childValue;
|
|
6596
6604
|
}
|
|
6597
6605
|
}
|
|
6598
6606
|
// Return flattened object if there are any values, otherwise undefined
|
|
@@ -6607,20 +6615,17 @@ function getFieldDefaultValue(field, registry) {
|
|
|
6607
6615
|
const groupDefaults = {};
|
|
6608
6616
|
for (const childField of fields) {
|
|
6609
6617
|
const childValueHandling = getFieldValueHandling(childField.type, registry);
|
|
6610
|
-
|
|
6611
|
-
|
|
6612
|
-
|
|
6613
|
-
|
|
6614
|
-
|
|
6615
|
-
|
|
6616
|
-
|
|
6617
|
-
|
|
6618
|
-
}
|
|
6619
|
-
}
|
|
6618
|
+
if (childValueHandling === 'exclude')
|
|
6619
|
+
continue;
|
|
6620
|
+
const childValue = getFieldDefaultValue(childField, registry);
|
|
6621
|
+
if (childValueHandling === 'flatten') {
|
|
6622
|
+
// Flatten container/row/page values into the group (they are presentational containers).
|
|
6623
|
+
// The recursive call already returns a flat object — spread it directly.
|
|
6624
|
+
if (childValue && typeof childValue === 'object' && !Array.isArray(childValue)) {
|
|
6625
|
+
Object.assign(groupDefaults, childValue);
|
|
6620
6626
|
}
|
|
6621
6627
|
}
|
|
6622
6628
|
else if ('key' in childField && childField.key) {
|
|
6623
|
-
const childValue = getFieldDefaultValue(childField, registry);
|
|
6624
6629
|
if (childValue !== undefined) {
|
|
6625
6630
|
groupDefaults[childField.key] = childValue;
|
|
6626
6631
|
}
|
|
@@ -7438,10 +7443,10 @@ function createEmptyCollection() {
|
|
|
7438
7443
|
/** Traverses field definitions and collects cross-field entries (validators, logic, schemas). */
|
|
7439
7444
|
function collectCrossFieldEntries(fields) {
|
|
7440
7445
|
const collection = createEmptyCollection();
|
|
7441
|
-
traverseFields
|
|
7446
|
+
traverseFields(fields, collection, '');
|
|
7442
7447
|
return collection;
|
|
7443
7448
|
}
|
|
7444
|
-
function traverseFields
|
|
7449
|
+
function traverseFields(fields, collection, pathPrefix) {
|
|
7445
7450
|
for (const field of fields) {
|
|
7446
7451
|
collectFromField$2(field, collection, pathPrefix);
|
|
7447
7452
|
// Recursively process container fields (page, row, group, array)
|
|
@@ -7453,7 +7458,7 @@ function traverseFields$2(fields, collection, pathPrefix) {
|
|
|
7453
7458
|
// be built for them.
|
|
7454
7459
|
const groupKey = isGroupField(field) ? field.key : undefined;
|
|
7455
7460
|
const childPrefix = groupKey ? (pathPrefix ? `${pathPrefix}.${groupKey}` : groupKey) : pathPrefix;
|
|
7456
|
-
traverseFields
|
|
7461
|
+
traverseFields(normalizeFieldsArray(field.fields), collection, childPrefix);
|
|
7457
7462
|
}
|
|
7458
7463
|
}
|
|
7459
7464
|
}
|
|
@@ -8168,6 +8173,29 @@ function buildPropertyOverrideKey(arrayKey, index, fieldKey) {
|
|
|
8168
8173
|
return fieldKey;
|
|
8169
8174
|
}
|
|
8170
8175
|
|
|
8176
|
+
/**
|
|
8177
|
+
* Returns true if the field has any property-derivation logic entries
|
|
8178
|
+
* (type: 'derivation' with a non-empty targetProperty). Mirrors the inclusion
|
|
8179
|
+
* rule used by `collectPropertyDerivations`, so the mapper and orchestrator
|
|
8180
|
+
* agree on which fields participate in property overrides — without depending
|
|
8181
|
+
* on whether the orchestrator's registration effect has run yet (which it
|
|
8182
|
+
* hasn't on the resolveFieldSync path with a warm COMPONENT_CACHE).
|
|
8183
|
+
*/
|
|
8184
|
+
function fieldHasPropertyDerivations(fieldDef) {
|
|
8185
|
+
// Match the collector's keyless guard (property-derivation-collector.ts):
|
|
8186
|
+
// keyless fields are never registered in the store, so the mapper must skip
|
|
8187
|
+
// them too — otherwise getOverrides() would be called with an undefined key.
|
|
8188
|
+
if (!fieldDef.key)
|
|
8189
|
+
return false;
|
|
8190
|
+
const logic = fieldDef.logic;
|
|
8191
|
+
if (!logic || logic.length === 0)
|
|
8192
|
+
return false;
|
|
8193
|
+
for (const entry of logic) {
|
|
8194
|
+
if (isDerivationLogicConfig(entry) && hasTargetProperty(entry))
|
|
8195
|
+
return true;
|
|
8196
|
+
}
|
|
8197
|
+
return false;
|
|
8198
|
+
}
|
|
8171
8199
|
/**
|
|
8172
8200
|
* Merges forwarded props into meta, with meta taking precedence.
|
|
8173
8201
|
*
|
|
@@ -8269,11 +8297,13 @@ function mapFieldToInputs(fieldDef, fieldRegistry) {
|
|
|
8269
8297
|
// Check that arrayContext exists and has a valid index signal (guards against mock injectors)
|
|
8270
8298
|
const indexSignal = arrayContext?.index;
|
|
8271
8299
|
const hasArrayContext = indexSignal !== undefined && isSignal(indexSignal);
|
|
8272
|
-
// Fast-path check for property overrides: only fields
|
|
8273
|
-
// enter the computed() wrapper
|
|
8274
|
-
//
|
|
8275
|
-
//
|
|
8276
|
-
|
|
8300
|
+
// Fast-path check for property overrides: only fields whose definition declares
|
|
8301
|
+
// a property-derivation enter the computed() wrapper. We inspect the FieldDef
|
|
8302
|
+
// directly rather than asking the store, because the store's registration
|
|
8303
|
+
// effect may not have fired yet when the mapper runs in the resolveFieldSync
|
|
8304
|
+
// path (warm COMPONENT_CACHE). Both the mapper and the orchestrator's
|
|
8305
|
+
// collector apply the same inclusion rule, so the answers always agree.
|
|
8306
|
+
const hasOverrides = fieldHasPropertyDerivations(fieldDef);
|
|
8277
8307
|
// Fast path: no transformations needed
|
|
8278
8308
|
if (!hasPropsForwarding && !hasArrayContext && !hasOverrides) {
|
|
8279
8309
|
return mapperResult;
|
|
@@ -8427,8 +8457,7 @@ function createFieldResolutionPipe(getContext) {
|
|
|
8427
8457
|
* Comprehensive form configuration validator that checks:
|
|
8428
8458
|
* 1. Form mode consistency (paged vs non-paged)
|
|
8429
8459
|
* 2. Page nesting rules
|
|
8430
|
-
* 3.
|
|
8431
|
-
* 4. Field placement constraints
|
|
8460
|
+
* 3. Field placement constraints
|
|
8432
8461
|
*/
|
|
8433
8462
|
class FormModeValidator {
|
|
8434
8463
|
/**
|
|
@@ -8495,57 +8524,6 @@ class FormModeValidator {
|
|
|
8495
8524
|
}
|
|
8496
8525
|
}
|
|
8497
8526
|
}
|
|
8498
|
-
// Warn about hidden fields in rows (they work but don't render, which may be confusing)
|
|
8499
|
-
const rowWarnings = this.collectRowHiddenFieldWarnings(fields);
|
|
8500
|
-
warnings.push(...rowWarnings);
|
|
8501
|
-
return warnings;
|
|
8502
|
-
}
|
|
8503
|
-
/**
|
|
8504
|
-
* Recursively collects warnings for hidden fields inside rows
|
|
8505
|
-
* @param fields The form field definitions to check
|
|
8506
|
-
* @param path Current path for warning messages
|
|
8507
|
-
* @returns Array of warning messages
|
|
8508
|
-
*/
|
|
8509
|
-
static collectRowHiddenFieldWarnings(fields, path = '') {
|
|
8510
|
-
if (!fields) {
|
|
8511
|
-
return [];
|
|
8512
|
-
}
|
|
8513
|
-
const warnings = [];
|
|
8514
|
-
for (let i = 0; i < fields.length; i++) {
|
|
8515
|
-
const field = fields[i];
|
|
8516
|
-
const fieldPath = path ? `${path}.fields[${i}]` : `fields[${i}]`;
|
|
8517
|
-
const fieldKey = field.key || 'unknown';
|
|
8518
|
-
if (isRowField(field)) {
|
|
8519
|
-
if (!validateRowNesting(field)) {
|
|
8520
|
-
warnings.push(`Row field at ${fieldPath} (key: "${fieldKey}") contains hidden fields. ` +
|
|
8521
|
-
`Hidden fields in rows don't render anything - consider placing them outside the row.`);
|
|
8522
|
-
}
|
|
8523
|
-
// Continue checking nested rows within the row's children
|
|
8524
|
-
warnings.push(...this.collectRowHiddenFieldWarnings(field.fields, fieldPath));
|
|
8525
|
-
}
|
|
8526
|
-
else if (isPageField(field)) {
|
|
8527
|
-
// Check rows within pages
|
|
8528
|
-
warnings.push(...this.collectRowHiddenFieldWarnings(field.fields, fieldPath));
|
|
8529
|
-
}
|
|
8530
|
-
else if (isGroupField(field)) {
|
|
8531
|
-
// Check rows within groups
|
|
8532
|
-
warnings.push(...this.collectRowHiddenFieldWarnings(field.fields, fieldPath));
|
|
8533
|
-
}
|
|
8534
|
-
else if (isSimplifiedArrayField(field)) {
|
|
8535
|
-
// Simplified arrays are normalized later; check template fields if present
|
|
8536
|
-
const templateFields = Array.isArray(field.template) ? [...field.template] : [field.template];
|
|
8537
|
-
warnings.push(...this.collectRowHiddenFieldWarnings(templateFields, fieldPath));
|
|
8538
|
-
}
|
|
8539
|
-
else if (isArrayField(field)) {
|
|
8540
|
-
// Check rows within array templates — items may be single FieldDef (primitive) or FieldDef[] (object)
|
|
8541
|
-
const itemTemplates = field.fields;
|
|
8542
|
-
for (let j = 0; j < itemTemplates.length; j++) {
|
|
8543
|
-
const itemFields = itemTemplates[j];
|
|
8544
|
-
const fieldsArray = Array.isArray(itemFields) ? [...itemFields] : [itemFields];
|
|
8545
|
-
warnings.push(...this.collectRowHiddenFieldWarnings(fieldsArray, `${fieldPath}.fields[${j}]`));
|
|
8546
|
-
}
|
|
8547
|
-
}
|
|
8548
|
-
}
|
|
8549
8527
|
return warnings;
|
|
8550
8528
|
}
|
|
8551
8529
|
}
|
|
@@ -10532,6 +10510,33 @@ function parseDerivationKey(key) {
|
|
|
10532
10510
|
return { fieldKey: key };
|
|
10533
10511
|
}
|
|
10534
10512
|
|
|
10513
|
+
/**
|
|
10514
|
+
* Single dispatch over `value` / `expression` / `functionName` shared by both
|
|
10515
|
+
* the value-derivation and property-derivation applicators.
|
|
10516
|
+
*
|
|
10517
|
+
* Throws {@link DynamicFormError} on missing function or missing value source
|
|
10518
|
+
* so both pipelines surface the library's standard error type.
|
|
10519
|
+
*
|
|
10520
|
+
* @internal
|
|
10521
|
+
*/
|
|
10522
|
+
function computeValueFromEntry(entry, evalContext, options) {
|
|
10523
|
+
if (entry.value !== undefined) {
|
|
10524
|
+
return entry.value;
|
|
10525
|
+
}
|
|
10526
|
+
if (entry.expression) {
|
|
10527
|
+
return ExpressionParser.evaluate(entry.expression, evalContext);
|
|
10528
|
+
}
|
|
10529
|
+
if (entry.functionName) {
|
|
10530
|
+
const fn = options.derivationFunctions?.[entry.functionName];
|
|
10531
|
+
if (!fn) {
|
|
10532
|
+
const kind = options.functionKind ?? 'Derivation function';
|
|
10533
|
+
throw new DynamicFormError(`${kind} '${entry.functionName}' not found in customFnConfig.derivations`);
|
|
10534
|
+
}
|
|
10535
|
+
return fn(evalContext);
|
|
10536
|
+
}
|
|
10537
|
+
throw new DynamicFormError(`Derivation for ${options.subject} has no value source. Specify 'value', 'expression', or 'functionName'.`);
|
|
10538
|
+
}
|
|
10539
|
+
|
|
10535
10540
|
/**
|
|
10536
10541
|
* Centralized constants for the derivation system.
|
|
10537
10542
|
*
|
|
@@ -11101,10 +11106,15 @@ function createEvaluationContext$1(entry, formValue, context, chainContext) {
|
|
|
11101
11106
|
// Create field state snapshot for this specific field (non-reactive)
|
|
11102
11107
|
const fieldAccessor = context.rootForm[entry.fieldKey];
|
|
11103
11108
|
const fieldState = fieldAccessor ? readFieldStateInfo(fieldAccessor, false) : undefined;
|
|
11109
|
+
// Parent group value: the absolute path with the trailing field segment
|
|
11110
|
+
// dropped. `undefined` when the field is at form root.
|
|
11111
|
+
const parentPath = getParentPathInScope(entry.fieldKey);
|
|
11112
|
+
const groupValue = parentPath ? getNestedValue(formValue, parentPath) : undefined;
|
|
11104
11113
|
return {
|
|
11105
11114
|
fieldValue,
|
|
11106
11115
|
formValue,
|
|
11107
11116
|
fieldPath: entry.fieldKey,
|
|
11117
|
+
groupValue,
|
|
11108
11118
|
customFunctions: context.customFunctions,
|
|
11109
11119
|
externalData: context.externalData,
|
|
11110
11120
|
logger: context.logger,
|
|
@@ -11112,6 +11122,25 @@ function createEvaluationContext$1(entry, formValue, context, chainContext) {
|
|
|
11112
11122
|
formFieldState: chainContext.formFieldState,
|
|
11113
11123
|
};
|
|
11114
11124
|
}
|
|
11125
|
+
/**
|
|
11126
|
+
* Returns the path to the parent of the given field path, expressed in the
|
|
11127
|
+
* scope appropriate for the entry. Drops the last `.`-delimited segment.
|
|
11128
|
+
* Returns `undefined` when the field has no parent in scope (e.g., a
|
|
11129
|
+
* single-segment root key, or `'items.$.x'` inside the array-item scope
|
|
11130
|
+
* where `x` is a direct child of the item).
|
|
11131
|
+
*
|
|
11132
|
+
* For non-array entries: `'address.state'` → `'address'`, `'state'` → undefined.
|
|
11133
|
+
* For array entries inside `createEvaluationContext` (root scope): unused —
|
|
11134
|
+
* `createArrayItemEvaluationContext` handles the array-scope case.
|
|
11135
|
+
*
|
|
11136
|
+
* @internal
|
|
11137
|
+
*/
|
|
11138
|
+
function getParentPathInScope(fieldKey) {
|
|
11139
|
+
const lastDot = fieldKey.lastIndexOf('.');
|
|
11140
|
+
if (lastDot <= 0)
|
|
11141
|
+
return undefined;
|
|
11142
|
+
return fieldKey.slice(0, lastDot);
|
|
11143
|
+
}
|
|
11115
11144
|
/**
|
|
11116
11145
|
* Creates an evaluation context scoped to a specific array item.
|
|
11117
11146
|
*
|
|
@@ -11143,10 +11172,23 @@ function createArrayItemEvaluationContext$1(entry, arrayItem, rootFormValue, ite
|
|
|
11143
11172
|
}
|
|
11144
11173
|
}
|
|
11145
11174
|
}
|
|
11175
|
+
// groupValue inside an array item:
|
|
11176
|
+
// - Field directly under the array item (no inner group): the array item
|
|
11177
|
+
// itself is the "nearest parent group".
|
|
11178
|
+
// - Field inside an inner group within the item: the inner group's value.
|
|
11179
|
+
// The relative path within the item is everything after `'.$.'`.
|
|
11180
|
+
const relativePath = pathInfo.isArrayPath ? (pathInfo.relativePath ?? '') : '';
|
|
11181
|
+
const innerParentPath = relativePath.includes('.') ? relativePath.slice(0, relativePath.lastIndexOf('.')) : '';
|
|
11182
|
+
const groupValue = innerParentPath ? getNestedValue(arrayItem, innerParentPath) : arrayItem;
|
|
11183
|
+
// Resolve fieldValue to the leaf value addressed by the entry's relative path
|
|
11184
|
+
// within the array item. Without this, $self / fieldValue derivations inside
|
|
11185
|
+
// arrays receive the whole array item instead of the field's own value.
|
|
11186
|
+
const fieldValue = relativePath ? getNestedValue(arrayItem, relativePath) : arrayItem;
|
|
11146
11187
|
return {
|
|
11147
|
-
fieldValue
|
|
11188
|
+
fieldValue,
|
|
11148
11189
|
formValue: arrayItem,
|
|
11149
|
-
fieldPath: `${arrayPath}.${itemIndex}`,
|
|
11190
|
+
fieldPath: relativePath ? `${arrayPath}.${itemIndex}.${relativePath}` : `${arrayPath}.${itemIndex}`,
|
|
11191
|
+
groupValue,
|
|
11150
11192
|
customFunctions: context.customFunctions,
|
|
11151
11193
|
externalData: context.externalData,
|
|
11152
11194
|
logger: context.logger,
|
|
@@ -11175,24 +11217,10 @@ function evaluateDerivationCondition(condition, context) {
|
|
|
11175
11217
|
* @internal
|
|
11176
11218
|
*/
|
|
11177
11219
|
function computeDerivedValue(entry, evalContext, applicatorContext) {
|
|
11178
|
-
|
|
11179
|
-
|
|
11180
|
-
|
|
11181
|
-
}
|
|
11182
|
-
// Expression
|
|
11183
|
-
if (entry.expression) {
|
|
11184
|
-
return ExpressionParser.evaluate(entry.expression, evalContext);
|
|
11185
|
-
}
|
|
11186
|
-
// Custom function
|
|
11187
|
-
if (entry.functionName) {
|
|
11188
|
-
const fn = applicatorContext.derivationFunctions?.[entry.functionName];
|
|
11189
|
-
if (!fn) {
|
|
11190
|
-
throw new Error(`Derivation function '${entry.functionName}' not found in customFnConfig.derivations`);
|
|
11191
|
-
}
|
|
11192
|
-
return fn(evalContext);
|
|
11193
|
-
}
|
|
11194
|
-
// No value source specified
|
|
11195
|
-
throw new Error(`Derivation for ${entry.fieldKey} has no value source. ` + `Specify 'value', 'expression', or 'functionName'.`);
|
|
11220
|
+
return computeValueFromEntry(entry, evalContext, {
|
|
11221
|
+
derivationFunctions: applicatorContext.derivationFunctions,
|
|
11222
|
+
subject: entry.fieldKey,
|
|
11223
|
+
});
|
|
11196
11224
|
}
|
|
11197
11225
|
/**
|
|
11198
11226
|
* Error message prefix for derivation-related errors.
|
|
@@ -11442,6 +11470,134 @@ function topologicalSort(entries) {
|
|
|
11442
11470
|
return sorted;
|
|
11443
11471
|
}
|
|
11444
11472
|
|
|
11473
|
+
/**
|
|
11474
|
+
* Extracts the union of field dependencies declared by a derivation config.
|
|
11475
|
+
*
|
|
11476
|
+
* Combines:
|
|
11477
|
+
* - Explicit `dependsOn` (when provided, it takes precedence over auto-detection)
|
|
11478
|
+
* - Expression-detected dependencies (only used when `dependsOn` is empty)
|
|
11479
|
+
* - `'*'` wildcard for `functionName` (only when `dependsOn` is empty)
|
|
11480
|
+
* - Condition expression dependencies (always included)
|
|
11481
|
+
*
|
|
11482
|
+
* @internal
|
|
11483
|
+
*/
|
|
11484
|
+
function extractDependenciesFromConfig(config) {
|
|
11485
|
+
const deps = new Set();
|
|
11486
|
+
if (config.dependsOn && config.dependsOn.length > 0) {
|
|
11487
|
+
config.dependsOn.forEach((dep) => deps.add(dep));
|
|
11488
|
+
}
|
|
11489
|
+
else {
|
|
11490
|
+
if (config.expression) {
|
|
11491
|
+
extractStringDependencies(config.expression).forEach((dep) => deps.add(dep));
|
|
11492
|
+
}
|
|
11493
|
+
if (config.functionName) {
|
|
11494
|
+
deps.add('*');
|
|
11495
|
+
}
|
|
11496
|
+
}
|
|
11497
|
+
if (config.condition && config.condition !== true) {
|
|
11498
|
+
extractExpressionDependencies(config.condition).forEach((dep) => deps.add(dep));
|
|
11499
|
+
}
|
|
11500
|
+
return Array.from(deps);
|
|
11501
|
+
}
|
|
11502
|
+
|
|
11503
|
+
/**
|
|
11504
|
+
* Recursively walks a field-definition tree, invoking `visitor` for each field.
|
|
11505
|
+
*
|
|
11506
|
+
* Owns the subtleties shared across derivation collectors:
|
|
11507
|
+
* - Detects container fields via `hasChildFields`.
|
|
11508
|
+
* - Falls back to the simplified-array Symbol-metadata template when an array
|
|
11509
|
+
* field has empty `fields` (the case for arrays initialized without a
|
|
11510
|
+
* starting `value` — only the metadata template carries their item shape).
|
|
11511
|
+
* - Flattens primitive (FieldDef) and object (FieldDef[]) array items into a
|
|
11512
|
+
* single child list before recursing.
|
|
11513
|
+
*
|
|
11514
|
+
* Callers customize per-boundary context via {@link FieldTraversalHooks}: e.g.,
|
|
11515
|
+
* the value-derivation collector tracks an array path AND a group path (with
|
|
11516
|
+
* the group path reset at array boundaries), while the property-derivation
|
|
11517
|
+
* collector tracks only the array path.
|
|
11518
|
+
*
|
|
11519
|
+
* @internal
|
|
11520
|
+
*/
|
|
11521
|
+
function traverseFieldsWithContext(fields, context, visitor, hooks) {
|
|
11522
|
+
for (const field of fields) {
|
|
11523
|
+
visitor(field, context);
|
|
11524
|
+
if (!hasChildFields(field))
|
|
11525
|
+
continue;
|
|
11526
|
+
if (field.type === 'array') {
|
|
11527
|
+
const childContext = mergeContext(context, hooks?.onArrayChild?.(context, field));
|
|
11528
|
+
const arrayChildren = collectArrayChildren(field);
|
|
11529
|
+
traverseFieldsWithContext(arrayChildren, childContext, visitor, hooks);
|
|
11530
|
+
continue;
|
|
11531
|
+
}
|
|
11532
|
+
const overrides = field.type === 'group' ? hooks?.onGroupChild?.(context, field) : hooks?.onLayoutChild?.(context, field);
|
|
11533
|
+
const childContext = mergeContext(context, overrides);
|
|
11534
|
+
traverseFieldsWithContext(normalizeFieldsArray(field.fields), childContext, visitor, hooks);
|
|
11535
|
+
}
|
|
11536
|
+
}
|
|
11537
|
+
/**
|
|
11538
|
+
* Returns the flattened list of children for an array field, falling back
|
|
11539
|
+
* to the Symbol metadata template when `fields` is empty.
|
|
11540
|
+
*
|
|
11541
|
+
* Caller must ensure `field` is a container (passes `hasChildFields`); this
|
|
11542
|
+
* function reads `field.fields` directly and would error otherwise.
|
|
11543
|
+
*
|
|
11544
|
+
* @internal
|
|
11545
|
+
*/
|
|
11546
|
+
function collectArrayChildren(field) {
|
|
11547
|
+
let arrayItems = normalizeFieldsArray(field.fields);
|
|
11548
|
+
if (arrayItems.length === 0) {
|
|
11549
|
+
const metadataTemplate = getNormalizedArrayMetadata(field)?.template;
|
|
11550
|
+
if (metadataTemplate) {
|
|
11551
|
+
arrayItems = [
|
|
11552
|
+
Array.isArray(metadataTemplate) ? [...metadataTemplate] : metadataTemplate,
|
|
11553
|
+
];
|
|
11554
|
+
}
|
|
11555
|
+
}
|
|
11556
|
+
const flattened = [];
|
|
11557
|
+
for (const item of arrayItems) {
|
|
11558
|
+
if (Array.isArray(item)) {
|
|
11559
|
+
flattened.push(...item);
|
|
11560
|
+
}
|
|
11561
|
+
else {
|
|
11562
|
+
flattened.push(item);
|
|
11563
|
+
}
|
|
11564
|
+
}
|
|
11565
|
+
return flattened;
|
|
11566
|
+
}
|
|
11567
|
+
/** @internal */
|
|
11568
|
+
function mergeContext(base, overrides) {
|
|
11569
|
+
return overrides ? { ...base, ...overrides } : { ...base };
|
|
11570
|
+
}
|
|
11571
|
+
|
|
11572
|
+
/**
|
|
11573
|
+
* Token in `dependsOn` arrays that resolves to the current field's own
|
|
11574
|
+
* absolute path at collection time. Useful for self-derivations on fields
|
|
11575
|
+
* nested under groups, where the absolute path depends on ancestor keys
|
|
11576
|
+
* the config author may not know (e.g. factory-built field shapes).
|
|
11577
|
+
*
|
|
11578
|
+
* @public
|
|
11579
|
+
*/
|
|
11580
|
+
const SELF_DEPENDENCY_TOKEN = '$self';
|
|
11581
|
+
/**
|
|
11582
|
+
* Token in `dependsOn` arrays that resolves to the absolute path of the
|
|
11583
|
+
* field's nearest parent container (group or array) at collection time.
|
|
11584
|
+
*
|
|
11585
|
+
* Useful when a derivation should fire whenever any sibling under the
|
|
11586
|
+
* same parent container changes (e.g. "compute when anything in the
|
|
11587
|
+
* address group is edited") without enumerating each sibling explicitly.
|
|
11588
|
+
*
|
|
11589
|
+
* Resolution rules:
|
|
11590
|
+
* - Inside group(s) only: resolves to the dotted ancestor group path
|
|
11591
|
+
* (e.g. `'address'` or `'org.address'`).
|
|
11592
|
+
* - Inside an array (no group between): resolves to the array key
|
|
11593
|
+
* (e.g. `'items'`). Fires whenever any item changes.
|
|
11594
|
+
* - Inside group(s) inside an array: resolves to the array placeholder
|
|
11595
|
+
* form including the inner groups (e.g. `'items.$.address'`).
|
|
11596
|
+
* - At form root: throws `DynamicFormError` (no parent to target).
|
|
11597
|
+
*
|
|
11598
|
+
* @public
|
|
11599
|
+
*/
|
|
11600
|
+
const GROUP_DEPENDENCY_TOKEN = '$group';
|
|
11445
11601
|
/**
|
|
11446
11602
|
* Collects all derivation entries from field definitions.
|
|
11447
11603
|
*
|
|
@@ -11479,8 +11635,21 @@ function topologicalSort(entries) {
|
|
|
11479
11635
|
*/
|
|
11480
11636
|
function collectDerivations(fields) {
|
|
11481
11637
|
const entries = [];
|
|
11482
|
-
|
|
11483
|
-
|
|
11638
|
+
traverseFieldsWithContext(fields, {}, (field, context) => collectFromField$1(field, entries, context), {
|
|
11639
|
+
// Array boundary: the array placeholder path takes over and any
|
|
11640
|
+
// ancestor groupPath is reset for descendants (entries inside
|
|
11641
|
+
// array items use `arrayPath.$.fieldKey` form).
|
|
11642
|
+
onArrayChild: (_, field) => ({ arrayPath: field.key, groupPath: undefined }),
|
|
11643
|
+
onGroupChild: (parent, field) => {
|
|
11644
|
+
// Group establishes a model boundary; descend with the field's
|
|
11645
|
+
// key appended to the ancestor groupPath. Groups without a key
|
|
11646
|
+
// (rare) act like layout containers — leave context unchanged.
|
|
11647
|
+
if (!field.key)
|
|
11648
|
+
return {};
|
|
11649
|
+
return { groupPath: parent.groupPath ? `${parent.groupPath}.${field.key}` : field.key };
|
|
11650
|
+
},
|
|
11651
|
+
// Layout containers (page, row, container) leave context unchanged.
|
|
11652
|
+
});
|
|
11484
11653
|
// Sort entries in topological order for efficient processing.
|
|
11485
11654
|
// This ensures derivations are processed in dependency order,
|
|
11486
11655
|
// reducing the number of iterations needed in the applicator.
|
|
@@ -11488,40 +11657,83 @@ function collectDerivations(fields) {
|
|
|
11488
11657
|
return { entries: sortedEntries };
|
|
11489
11658
|
}
|
|
11490
11659
|
/**
|
|
11491
|
-
*
|
|
11660
|
+
* Builds the absolute field path for a derivation entry from the field's
|
|
11661
|
+
* key and the current collection context.
|
|
11662
|
+
*
|
|
11663
|
+
* - Inside an array AND inside one or more groups (groups nested under
|
|
11664
|
+
* the array item): combines both as `${arrayPath}.$.${groupPath}.${fieldKey}`.
|
|
11665
|
+
* - Inside an array only: returns `${arrayPath}.$.${fieldKey}`.
|
|
11666
|
+
* - Inside one or more groups: returns `${groupPath}.${fieldKey}`.
|
|
11667
|
+
* - Otherwise returns `fieldKey` unchanged.
|
|
11668
|
+
*
|
|
11669
|
+
* `groupPath` is reset when entering an array (each array item is its own
|
|
11670
|
+
* model root); ancestor groups OUTSIDE the array are not part of the
|
|
11671
|
+
* entry path. Groups INSIDE the array are tracked via `groupPath` and
|
|
11672
|
+
* combined here.
|
|
11492
11673
|
*
|
|
11493
11674
|
* @internal
|
|
11494
11675
|
*/
|
|
11495
|
-
function
|
|
11496
|
-
|
|
11497
|
-
|
|
11498
|
-
|
|
11499
|
-
|
|
11500
|
-
|
|
11501
|
-
|
|
11502
|
-
|
|
11503
|
-
|
|
11504
|
-
|
|
11505
|
-
|
|
11506
|
-
|
|
11507
|
-
|
|
11508
|
-
|
|
11509
|
-
|
|
11510
|
-
|
|
11511
|
-
|
|
11512
|
-
|
|
11513
|
-
|
|
11514
|
-
|
|
11515
|
-
|
|
11516
|
-
|
|
11517
|
-
|
|
11518
|
-
|
|
11519
|
-
|
|
11520
|
-
|
|
11521
|
-
|
|
11676
|
+
function buildEffectiveFieldKey(fieldKey, context) {
|
|
11677
|
+
if (context.arrayPath && context.groupPath) {
|
|
11678
|
+
return `${context.arrayPath}.$.${context.groupPath}.${fieldKey}`;
|
|
11679
|
+
}
|
|
11680
|
+
if (context.arrayPath) {
|
|
11681
|
+
return `${context.arrayPath}.$.${fieldKey}`;
|
|
11682
|
+
}
|
|
11683
|
+
if (context.groupPath) {
|
|
11684
|
+
return `${context.groupPath}.${fieldKey}`;
|
|
11685
|
+
}
|
|
11686
|
+
return fieldKey;
|
|
11687
|
+
}
|
|
11688
|
+
/**
|
|
11689
|
+
* Resolves the absolute path of the field's nearest parent container
|
|
11690
|
+
* (group or array). Returns `undefined` when the field is at the form
|
|
11691
|
+
* root with no parent container.
|
|
11692
|
+
*
|
|
11693
|
+
* @internal
|
|
11694
|
+
*/
|
|
11695
|
+
function buildNearestGroupPath(context) {
|
|
11696
|
+
if (context.arrayPath && context.groupPath) {
|
|
11697
|
+
return `${context.arrayPath}.$.${context.groupPath}`;
|
|
11698
|
+
}
|
|
11699
|
+
return context.arrayPath ?? context.groupPath;
|
|
11700
|
+
}
|
|
11701
|
+
/**
|
|
11702
|
+
* Substitutes special tokens in a `dependsOn` array with their resolved
|
|
11703
|
+
* absolute paths. Currently supports:
|
|
11704
|
+
*
|
|
11705
|
+
* - `SELF_DEPENDENCY_TOKEN` ('$self') → the field's own absolute path
|
|
11706
|
+
* - `'$self.X'` → the field's own path joined with `.X` (e.g. for object-valued
|
|
11707
|
+
* fields where you want to depend on a sub-property)
|
|
11708
|
+
* - `GROUP_DEPENDENCY_TOKEN` ('$group') → the field's nearest parent
|
|
11709
|
+
* container path; throws if the field has no parent group/array.
|
|
11710
|
+
* - `'$group.sibling'` → the parent path joined with `.sibling`, useful to
|
|
11711
|
+
* target a specific sibling without naming the parent group key.
|
|
11712
|
+
*
|
|
11713
|
+
* Non-token strings are passed through unchanged.
|
|
11714
|
+
*
|
|
11715
|
+
* @internal
|
|
11716
|
+
*/
|
|
11717
|
+
function resolveTokenDependencies(deps, effectiveFieldKey, context) {
|
|
11718
|
+
return deps.map((dep) => {
|
|
11719
|
+
if (dep === SELF_DEPENDENCY_TOKEN) {
|
|
11720
|
+
return effectiveFieldKey;
|
|
11721
|
+
}
|
|
11722
|
+
if (dep.startsWith(`${SELF_DEPENDENCY_TOKEN}.`)) {
|
|
11723
|
+
return effectiveFieldKey + dep.slice(SELF_DEPENDENCY_TOKEN.length);
|
|
11724
|
+
}
|
|
11725
|
+
if (dep === GROUP_DEPENDENCY_TOKEN || dep.startsWith(`${GROUP_DEPENDENCY_TOKEN}.`)) {
|
|
11726
|
+
const groupPath = buildNearestGroupPath(context);
|
|
11727
|
+
if (!groupPath) {
|
|
11728
|
+
throw new DynamicFormError(`Derivation for '${effectiveFieldKey}' uses '${dep}' but the field has no parent group or array. ` +
|
|
11729
|
+
`'${GROUP_DEPENDENCY_TOKEN}' is only valid for fields nested inside a group or array container.`);
|
|
11522
11730
|
}
|
|
11731
|
+
if (dep === GROUP_DEPENDENCY_TOKEN)
|
|
11732
|
+
return groupPath;
|
|
11733
|
+
return groupPath + dep.slice(GROUP_DEPENDENCY_TOKEN.length);
|
|
11523
11734
|
}
|
|
11524
|
-
|
|
11735
|
+
return dep;
|
|
11736
|
+
});
|
|
11525
11737
|
}
|
|
11526
11738
|
/**
|
|
11527
11739
|
* Collects derivation entries from a single field.
|
|
@@ -11561,8 +11773,7 @@ function collectFromField$1(field, entries, context) {
|
|
|
11561
11773
|
* @internal
|
|
11562
11774
|
*/
|
|
11563
11775
|
function createShorthandEntry(fieldKey, expression, context) {
|
|
11564
|
-
|
|
11565
|
-
const effectiveFieldKey = context.arrayPath ? `${context.arrayPath}.$.${fieldKey}` : fieldKey;
|
|
11776
|
+
const effectiveFieldKey = buildEffectiveFieldKey(fieldKey, context);
|
|
11566
11777
|
return {
|
|
11567
11778
|
fieldKey: effectiveFieldKey,
|
|
11568
11779
|
dependsOn: extractStringDependencies(expression),
|
|
@@ -11588,11 +11799,16 @@ function createShorthandEntry(fieldKey, expression, context) {
|
|
|
11588
11799
|
* @internal
|
|
11589
11800
|
*/
|
|
11590
11801
|
function createLogicEntry(fieldKey, config, context) {
|
|
11591
|
-
|
|
11592
|
-
const effectiveFieldKey = context.arrayPath ? `${context.arrayPath}.$.${fieldKey}` : fieldKey;
|
|
11802
|
+
const effectiveFieldKey = buildEffectiveFieldKey(fieldKey, context);
|
|
11593
11803
|
// Runtime guards for HTTP and async derivations.
|
|
11594
11804
|
// Mutual exclusivity and required fields are now enforced by TypeScript (via `source` discriminant).
|
|
11595
11805
|
// The wildcard and empty-array checks below are not expressible in the type system.
|
|
11806
|
+
//
|
|
11807
|
+
// Note: `$group` / `$group.X` tokens are intentionally allowed for HTTP and async sources even
|
|
11808
|
+
// though they resolve to the parent path and therefore fire on any sibling change inside that
|
|
11809
|
+
// group/array. Unlike `*`, the token is structurally scoped (collection throws if the field has
|
|
11810
|
+
// no parent container) and an explicit opt-in. Consumers that want to bound request frequency
|
|
11811
|
+
// can use `trigger: 'debounced'` here, or rely on debouncing inside their async function.
|
|
11596
11812
|
if (config.source === 'http') {
|
|
11597
11813
|
if (config.dependsOn.length === 0) {
|
|
11598
11814
|
throw new DynamicFormError(`HTTP derivation for '${effectiveFieldKey}' requires explicit 'dependsOn'. ` +
|
|
@@ -11613,7 +11829,7 @@ function createLogicEntry(fieldKey, config, context) {
|
|
|
11613
11829
|
`Wildcards would trigger async functions on every form change. Specify explicit field dependencies instead.`);
|
|
11614
11830
|
}
|
|
11615
11831
|
}
|
|
11616
|
-
const dependsOn =
|
|
11832
|
+
const dependsOn = resolveTokenDependencies(extractDependenciesFromConfig(config), effectiveFieldKey, context);
|
|
11617
11833
|
const condition = config.condition ?? true;
|
|
11618
11834
|
const trigger = config.trigger ?? 'onChange';
|
|
11619
11835
|
// Extract debounceMs from debounced configs
|
|
@@ -11638,43 +11854,6 @@ function createLogicEntry(fieldKey, config, context) {
|
|
|
11638
11854
|
reEngageOnDependencyChange: config.reEngageOnDependencyChange,
|
|
11639
11855
|
};
|
|
11640
11856
|
}
|
|
11641
|
-
/**
|
|
11642
|
-
* Extracts all field dependencies from a derivation config.
|
|
11643
|
-
*
|
|
11644
|
-
* Combines dependencies from:
|
|
11645
|
-
* - Explicit `dependsOn` array (if provided, takes precedence)
|
|
11646
|
-
* - Condition expression
|
|
11647
|
-
* - Value expression
|
|
11648
|
-
* - Function name (defaults to '*' if no explicit dependsOn)
|
|
11649
|
-
*
|
|
11650
|
-
* @internal
|
|
11651
|
-
*/
|
|
11652
|
-
function extractDependencies$1(config) {
|
|
11653
|
-
const deps = new Set();
|
|
11654
|
-
// If explicit dependsOn is provided, use it as the primary source
|
|
11655
|
-
// This allows users to override automatic detection and control
|
|
11656
|
-
// when derivations are triggered
|
|
11657
|
-
if (config.dependsOn && config.dependsOn.length > 0) {
|
|
11658
|
-
config.dependsOn.forEach((dep) => deps.add(dep));
|
|
11659
|
-
}
|
|
11660
|
-
else {
|
|
11661
|
-
// Extract from expression (automatic dependency detection)
|
|
11662
|
-
if (config.expression) {
|
|
11663
|
-
const exprDeps = extractStringDependencies(config.expression);
|
|
11664
|
-
exprDeps.forEach((dep) => deps.add(dep));
|
|
11665
|
-
}
|
|
11666
|
-
// Custom functions assume full form access if no explicit dependsOn
|
|
11667
|
-
if (config.functionName) {
|
|
11668
|
-
deps.add('*');
|
|
11669
|
-
}
|
|
11670
|
-
}
|
|
11671
|
-
// Always extract from condition (these are additional runtime guards)
|
|
11672
|
-
if (config.condition && config.condition !== true) {
|
|
11673
|
-
const conditionDeps = extractExpressionDependencies(config.condition);
|
|
11674
|
-
conditionDeps.forEach((dep) => deps.add(dep));
|
|
11675
|
-
}
|
|
11676
|
-
return Array.from(deps);
|
|
11677
|
-
}
|
|
11678
11857
|
|
|
11679
11858
|
/**
|
|
11680
11859
|
* Detects cycles in the derivation dependency graph.
|
|
@@ -12361,6 +12540,58 @@ function createAsyncDerivationStream(entry, formValue$, context) {
|
|
|
12361
12540
|
}));
|
|
12362
12541
|
}
|
|
12363
12542
|
|
|
12543
|
+
/**
|
|
12544
|
+
* Returns the unique set of debounce periods declared by `entries` whose
|
|
12545
|
+
* `trigger === 'debounced'`. Entries without an explicit `debounceMs` use
|
|
12546
|
+
* {@link DEFAULT_DEBOUNCE_MS}.
|
|
12547
|
+
*
|
|
12548
|
+
* @internal
|
|
12549
|
+
*/
|
|
12550
|
+
function getDebouncePeriods(entries) {
|
|
12551
|
+
const periods = new Set();
|
|
12552
|
+
for (const entry of entries) {
|
|
12553
|
+
if (entry.trigger === 'debounced') {
|
|
12554
|
+
periods.add(entry.debounceMs ?? DEFAULT_DEBOUNCE_MS);
|
|
12555
|
+
}
|
|
12556
|
+
}
|
|
12557
|
+
return Array.from(periods);
|
|
12558
|
+
}
|
|
12559
|
+
/**
|
|
12560
|
+
* Filters `entries` down to those whose `trigger === 'debounced'` and whose
|
|
12561
|
+
* effective `debounceMs` (defaulting to {@link DEFAULT_DEBOUNCE_MS}) equals `period`.
|
|
12562
|
+
*
|
|
12563
|
+
* @internal
|
|
12564
|
+
*/
|
|
12565
|
+
function filterEntriesByDebouncePeriod(entries, period) {
|
|
12566
|
+
return entries.filter((entry) => entry.trigger === 'debounced' && (entry.debounceMs ?? DEFAULT_DEBOUNCE_MS) === period);
|
|
12567
|
+
}
|
|
12568
|
+
|
|
12569
|
+
/**
|
|
12570
|
+
* Resolves a record of external-data signals to their current values without
|
|
12571
|
+
* establishing reactive dependencies.
|
|
12572
|
+
*
|
|
12573
|
+
* The orchestrators read external data per derivation cycle; using `untracked`
|
|
12574
|
+
* here ensures the orchestrator's own computed/effect graph isn't tied to the
|
|
12575
|
+
* external-data signal identities.
|
|
12576
|
+
*
|
|
12577
|
+
* @internal
|
|
12578
|
+
*/
|
|
12579
|
+
function resolveExternalData(externalDataSignal) {
|
|
12580
|
+
const externalDataRecord = untracked(() => externalDataSignal?.());
|
|
12581
|
+
if (!externalDataRecord)
|
|
12582
|
+
return undefined;
|
|
12583
|
+
const resolved = {};
|
|
12584
|
+
for (const [key, value] of Object.entries(externalDataRecord)) {
|
|
12585
|
+
if (isSignal(value)) {
|
|
12586
|
+
resolved[key] = untracked(() => value());
|
|
12587
|
+
}
|
|
12588
|
+
else {
|
|
12589
|
+
resolved[key] = value;
|
|
12590
|
+
}
|
|
12591
|
+
}
|
|
12592
|
+
return resolved;
|
|
12593
|
+
}
|
|
12594
|
+
|
|
12364
12595
|
/**
|
|
12365
12596
|
* Orchestrates derivation processing for a dynamic form.
|
|
12366
12597
|
*
|
|
@@ -12477,7 +12708,7 @@ class DerivationOrchestrator {
|
|
|
12477
12708
|
if (!collection || !formAccessor)
|
|
12478
12709
|
return of(null);
|
|
12479
12710
|
// Get unique debounce periods from entries with trigger 'debounced'
|
|
12480
|
-
const debouncePeriods =
|
|
12711
|
+
const debouncePeriods = getDebouncePeriods(collection.entries);
|
|
12481
12712
|
if (debouncePeriods.length === 0)
|
|
12482
12713
|
return of(null);
|
|
12483
12714
|
// merge: Process multiple debounce periods concurrently.
|
|
@@ -12510,7 +12741,7 @@ class DerivationOrchestrator {
|
|
|
12510
12741
|
rootForm: formAccessor,
|
|
12511
12742
|
derivationFunctions: this.functionRegistry.getDerivationFunctions(),
|
|
12512
12743
|
customFunctions: this.functionRegistry.getCustomFunctions(),
|
|
12513
|
-
externalData: this.
|
|
12744
|
+
externalData: resolveExternalData(this.config.externalData),
|
|
12514
12745
|
logger: this.logger,
|
|
12515
12746
|
warningTracker: this.warningTracker,
|
|
12516
12747
|
derivationLogger: untracked(() => this.config.derivationLogger()),
|
|
@@ -12538,7 +12769,7 @@ class DerivationOrchestrator {
|
|
|
12538
12769
|
rootForm: formAccessor,
|
|
12539
12770
|
derivationFunctions: this.functionRegistry.getDerivationFunctions(),
|
|
12540
12771
|
customFunctions: this.functionRegistry.getCustomFunctions(),
|
|
12541
|
-
externalData: this.
|
|
12772
|
+
externalData: resolveExternalData(this.config.externalData),
|
|
12542
12773
|
logger: this.logger,
|
|
12543
12774
|
warningTracker: this.warningTracker,
|
|
12544
12775
|
derivationLogger: untracked(() => this.config.derivationLogger()),
|
|
@@ -12588,7 +12819,7 @@ class DerivationOrchestrator {
|
|
|
12588
12819
|
logger: this.logger,
|
|
12589
12820
|
derivationLogger: this.config.derivationLogger,
|
|
12590
12821
|
customFunctions: () => this.functionRegistry.getCustomFunctions(),
|
|
12591
|
-
externalData: () => this.
|
|
12822
|
+
externalData: () => resolveExternalData(this.config.externalData),
|
|
12592
12823
|
warningTracker: this.warningTracker,
|
|
12593
12824
|
guard$: this.streamGuard.guard$,
|
|
12594
12825
|
};
|
|
@@ -12662,7 +12893,7 @@ class DerivationOrchestrator {
|
|
|
12662
12893
|
derivationLogger: this.config.derivationLogger,
|
|
12663
12894
|
customFunctions: () => this.functionRegistry.getCustomFunctions(),
|
|
12664
12895
|
asyncDerivationFunctions: () => this.functionRegistry.getAsyncDerivationFunctions(),
|
|
12665
|
-
externalData: () => this.
|
|
12896
|
+
externalData: () => resolveExternalData(this.config.externalData),
|
|
12666
12897
|
warningTracker: this.warningTracker,
|
|
12667
12898
|
};
|
|
12668
12899
|
const stream = createAsyncDerivationStream(entry, formValue$, context).pipe(takeUntilDestroyed(this.destroyRef));
|
|
@@ -12697,42 +12928,6 @@ class DerivationOrchestrator {
|
|
|
12697
12928
|
return `${entry.fieldKey}:${JSON.stringify(config, Object.keys(config).sort())}`;
|
|
12698
12929
|
}));
|
|
12699
12930
|
}
|
|
12700
|
-
/**
|
|
12701
|
-
* Gets all unique debounce periods from entries with trigger 'debounced'.
|
|
12702
|
-
*/
|
|
12703
|
-
getDebouncePeriods(entries) {
|
|
12704
|
-
const periods = new Set();
|
|
12705
|
-
for (const entry of entries) {
|
|
12706
|
-
if (entry.trigger === 'debounced') {
|
|
12707
|
-
periods.add(entry.debounceMs ?? DEFAULT_DEBOUNCE_MS);
|
|
12708
|
-
}
|
|
12709
|
-
}
|
|
12710
|
-
return Array.from(periods);
|
|
12711
|
-
}
|
|
12712
|
-
/**
|
|
12713
|
-
* Resolves external data signals to their current values without creating dependencies.
|
|
12714
|
-
*
|
|
12715
|
-
* Uses `untracked()` to read signals without establishing reactive dependencies,
|
|
12716
|
-
* which is important for derivations that shouldn't re-trigger on every external data change.
|
|
12717
|
-
*
|
|
12718
|
-
* @returns Record of resolved external data values, or undefined if no external data.
|
|
12719
|
-
*/
|
|
12720
|
-
resolveExternalData() {
|
|
12721
|
-
const externalDataRecord = untracked(() => this.config.externalData?.());
|
|
12722
|
-
if (!externalDataRecord) {
|
|
12723
|
-
return undefined;
|
|
12724
|
-
}
|
|
12725
|
-
const resolved = {};
|
|
12726
|
-
for (const [key, signal] of Object.entries(externalDataRecord)) {
|
|
12727
|
-
if (isSignal(signal)) {
|
|
12728
|
-
resolved[key] = untracked(() => signal());
|
|
12729
|
-
}
|
|
12730
|
-
else {
|
|
12731
|
-
resolved[key] = signal;
|
|
12732
|
-
}
|
|
12733
|
-
}
|
|
12734
|
-
return resolved;
|
|
12735
|
-
}
|
|
12736
12931
|
}
|
|
12737
12932
|
/**
|
|
12738
12933
|
* Creates a DerivationOrchestrator for a dynamic form.
|
|
@@ -13219,24 +13414,11 @@ function evaluatePropertyCondition(condition, context) {
|
|
|
13219
13414
|
* @internal
|
|
13220
13415
|
*/
|
|
13221
13416
|
function computePropertyValue(entry, evalContext, applicatorContext) {
|
|
13222
|
-
|
|
13223
|
-
|
|
13224
|
-
|
|
13225
|
-
|
|
13226
|
-
|
|
13227
|
-
if (entry.expression) {
|
|
13228
|
-
return ExpressionParser.evaluate(entry.expression, evalContext);
|
|
13229
|
-
}
|
|
13230
|
-
// Custom function
|
|
13231
|
-
if (entry.functionName) {
|
|
13232
|
-
const fn = applicatorContext.derivationFunctions?.[entry.functionName];
|
|
13233
|
-
if (!fn) {
|
|
13234
|
-
throw new DynamicFormError(`Property derivation function '${entry.functionName}' not found in customFnConfig.derivations`);
|
|
13235
|
-
}
|
|
13236
|
-
return fn(evalContext);
|
|
13237
|
-
}
|
|
13238
|
-
throw new DynamicFormError(`Property derivation for ${entry.fieldKey}.${entry.targetProperty} has no value source. ` +
|
|
13239
|
-
`Specify 'value', 'expression', or 'functionName'.`);
|
|
13417
|
+
return computeValueFromEntry(entry, evalContext, {
|
|
13418
|
+
derivationFunctions: applicatorContext.derivationFunctions,
|
|
13419
|
+
subject: `${entry.fieldKey}.${entry.targetProperty}`,
|
|
13420
|
+
functionKind: 'Property derivation function',
|
|
13421
|
+
});
|
|
13240
13422
|
}
|
|
13241
13423
|
|
|
13242
13424
|
/**
|
|
@@ -13256,40 +13438,12 @@ function computePropertyValue(entry, evalContext, applicatorContext) {
|
|
|
13256
13438
|
function collectPropertyDerivations(fields, logger, tracker) {
|
|
13257
13439
|
const entries = [];
|
|
13258
13440
|
const context = { logger, tracker };
|
|
13259
|
-
|
|
13441
|
+
traverseFieldsWithContext(fields, context, (field, ctx) => collectFromField(field, entries, ctx), {
|
|
13442
|
+
onArrayChild: (_, field) => ({ arrayPath: field.key }),
|
|
13443
|
+
// Group and layout containers do not affect property-derivation paths.
|
|
13444
|
+
});
|
|
13260
13445
|
return { entries };
|
|
13261
13446
|
}
|
|
13262
|
-
/**
|
|
13263
|
-
* Recursively traverses field definitions to collect property derivations.
|
|
13264
|
-
*
|
|
13265
|
-
* @internal
|
|
13266
|
-
*/
|
|
13267
|
-
function traverseFields(fields, entries, context) {
|
|
13268
|
-
for (const field of fields) {
|
|
13269
|
-
collectFromField(field, entries, context);
|
|
13270
|
-
// Recursively process container fields (page, row, group, array)
|
|
13271
|
-
if (hasChildFields(field)) {
|
|
13272
|
-
const childContext = { ...context };
|
|
13273
|
-
if (field.type === 'array') {
|
|
13274
|
-
childContext.arrayPath = field.key;
|
|
13275
|
-
const arrayItems = normalizeFieldsArray(field.fields);
|
|
13276
|
-
const normalizedChildren = [];
|
|
13277
|
-
for (const item of arrayItems) {
|
|
13278
|
-
if (Array.isArray(item)) {
|
|
13279
|
-
normalizedChildren.push(...item);
|
|
13280
|
-
}
|
|
13281
|
-
else {
|
|
13282
|
-
normalizedChildren.push(item);
|
|
13283
|
-
}
|
|
13284
|
-
}
|
|
13285
|
-
traverseFields(normalizedChildren, entries, childContext);
|
|
13286
|
-
}
|
|
13287
|
-
else {
|
|
13288
|
-
traverseFields(normalizeFieldsArray(field.fields), entries, childContext);
|
|
13289
|
-
}
|
|
13290
|
-
}
|
|
13291
|
-
}
|
|
13292
|
-
}
|
|
13293
13447
|
/**
|
|
13294
13448
|
* Collects property derivation entries from a single field.
|
|
13295
13449
|
*
|
|
@@ -13319,7 +13473,7 @@ function collectFromField(field, entries, context) {
|
|
|
13319
13473
|
*/
|
|
13320
13474
|
function createPropertyDerivationEntryFromDerivation(fieldKey, config, context) {
|
|
13321
13475
|
const effectiveFieldKey = buildPropertyOverrideKey(context.arrayPath, context.arrayPath ? PLACEHOLDER_INDEX : undefined, fieldKey);
|
|
13322
|
-
const dependsOn =
|
|
13476
|
+
const dependsOn = extractDependenciesFromConfig(config);
|
|
13323
13477
|
const condition = config.condition ?? true;
|
|
13324
13478
|
const trigger = config.trigger ?? 'onChange';
|
|
13325
13479
|
const debounceMs = trigger === 'debounced' ? config.debounceMs : undefined;
|
|
@@ -13337,32 +13491,6 @@ function createPropertyDerivationEntryFromDerivation(fieldKey, config, context)
|
|
|
13337
13491
|
originalConfig: config,
|
|
13338
13492
|
};
|
|
13339
13493
|
}
|
|
13340
|
-
/**
|
|
13341
|
-
* Extracts all field dependencies from a property derivation config.
|
|
13342
|
-
*
|
|
13343
|
-
* @internal
|
|
13344
|
-
*/
|
|
13345
|
-
function extractDependencies(config) {
|
|
13346
|
-
const deps = new Set();
|
|
13347
|
-
if (config.dependsOn && config.dependsOn.length > 0) {
|
|
13348
|
-
config.dependsOn.forEach((dep) => deps.add(dep));
|
|
13349
|
-
}
|
|
13350
|
-
else {
|
|
13351
|
-
if (config.expression) {
|
|
13352
|
-
const exprDeps = extractStringDependencies(config.expression);
|
|
13353
|
-
exprDeps.forEach((dep) => deps.add(dep));
|
|
13354
|
-
}
|
|
13355
|
-
if (config.functionName) {
|
|
13356
|
-
deps.add('*');
|
|
13357
|
-
}
|
|
13358
|
-
}
|
|
13359
|
-
// Always extract from condition
|
|
13360
|
-
if (config.condition && config.condition !== true) {
|
|
13361
|
-
const conditionDeps = extractExpressionDependencies(config.condition);
|
|
13362
|
-
conditionDeps.forEach((dep) => deps.add(dep));
|
|
13363
|
-
}
|
|
13364
|
-
return Array.from(deps);
|
|
13365
|
-
}
|
|
13366
13494
|
|
|
13367
13495
|
/**
|
|
13368
13496
|
* Orchestrates property derivation processing for a dynamic form.
|
|
@@ -13440,7 +13568,7 @@ class PropertyDerivationOrchestrator {
|
|
|
13440
13568
|
const collection = untracked(() => this.propertyDerivationCollection());
|
|
13441
13569
|
if (!collection)
|
|
13442
13570
|
return of(null);
|
|
13443
|
-
const debouncePeriods =
|
|
13571
|
+
const debouncePeriods = getDebouncePeriods(collection.entries);
|
|
13444
13572
|
if (debouncePeriods.length === 0)
|
|
13445
13573
|
return of(null);
|
|
13446
13574
|
const periodStreams = debouncePeriods.map((debounceMs) => this.createPeriodStream(debounceMs, collection, changedFields));
|
|
@@ -13465,7 +13593,7 @@ class PropertyDerivationOrchestrator {
|
|
|
13465
13593
|
store: this.config.store,
|
|
13466
13594
|
derivationFunctions: this.functionRegistry.getDerivationFunctions(),
|
|
13467
13595
|
customFunctions: this.functionRegistry.getCustomFunctions(),
|
|
13468
|
-
externalData: this.
|
|
13596
|
+
externalData: resolveExternalData(this.config.externalData),
|
|
13469
13597
|
logger: this.logger,
|
|
13470
13598
|
warningTracker: this.warningTracker,
|
|
13471
13599
|
};
|
|
@@ -13481,45 +13609,12 @@ class PropertyDerivationOrchestrator {
|
|
|
13481
13609
|
store: this.config.store,
|
|
13482
13610
|
derivationFunctions: this.functionRegistry.getDerivationFunctions(),
|
|
13483
13611
|
customFunctions: this.functionRegistry.getCustomFunctions(),
|
|
13484
|
-
externalData: this.
|
|
13612
|
+
externalData: resolveExternalData(this.config.externalData),
|
|
13485
13613
|
logger: this.logger,
|
|
13486
13614
|
warningTracker: this.warningTracker,
|
|
13487
13615
|
};
|
|
13488
13616
|
applyPropertyDerivationsForTrigger(filteredCollection, 'debounced', applicatorContext, changedFields);
|
|
13489
13617
|
}
|
|
13490
|
-
getDebouncePeriods(entries) {
|
|
13491
|
-
const periods = new Set();
|
|
13492
|
-
for (const entry of entries) {
|
|
13493
|
-
if (entry.trigger === 'debounced') {
|
|
13494
|
-
periods.add(entry.debounceMs ?? DEFAULT_DEBOUNCE_MS);
|
|
13495
|
-
}
|
|
13496
|
-
}
|
|
13497
|
-
return Array.from(periods);
|
|
13498
|
-
}
|
|
13499
|
-
/**
|
|
13500
|
-
* Resolves external data signals to their current values.
|
|
13501
|
-
*
|
|
13502
|
-
* Called on every onChange and debounced application. Each call iterates all
|
|
13503
|
-
* external data entries and resolves signals via untracked(). For forms with
|
|
13504
|
-
* many external data entries, this could be optimized by caching the resolved
|
|
13505
|
-
* record and only invalidating when external data signal values change.
|
|
13506
|
-
* In practice, external data entries are few, so the linear scan is acceptable.
|
|
13507
|
-
*/
|
|
13508
|
-
resolveExternalData() {
|
|
13509
|
-
const externalDataRecord = untracked(() => this.config.externalData?.());
|
|
13510
|
-
if (!externalDataRecord)
|
|
13511
|
-
return undefined;
|
|
13512
|
-
const resolved = {};
|
|
13513
|
-
for (const [key, signalValue] of Object.entries(externalDataRecord)) {
|
|
13514
|
-
if (isSignal(signalValue)) {
|
|
13515
|
-
resolved[key] = untracked(() => signalValue());
|
|
13516
|
-
}
|
|
13517
|
-
else {
|
|
13518
|
-
resolved[key] = signalValue;
|
|
13519
|
-
}
|
|
13520
|
-
}
|
|
13521
|
-
return resolved;
|
|
13522
|
-
}
|
|
13523
13618
|
}
|
|
13524
13619
|
/**
|
|
13525
13620
|
* Creates a PropertyDerivationOrchestrator for a dynamic form.
|
|
@@ -14491,7 +14586,10 @@ function rowFieldMapper(fieldDef) {
|
|
|
14491
14586
|
// Rebuilt each emission so logic-driven updates to fieldDef (e.g. `disabled`)
|
|
14492
14587
|
// flow through. Shallow spread is safe while RowField has no nested
|
|
14493
14588
|
// mutable props; add a deep-clone if that stops being true.
|
|
14494
|
-
|
|
14589
|
+
// `type` is rewritten to 'container' because `row` is a synthetic field
|
|
14590
|
+
// type — the runtime renders it as a ContainerField with a synthesized
|
|
14591
|
+
// `{ type: 'row' }` wrapper.
|
|
14592
|
+
const containerField = { ...fieldDef, type: 'container', wrappers: ROW_WRAPPERS };
|
|
14495
14593
|
const inputs = {
|
|
14496
14594
|
key: fieldDef.key,
|
|
14497
14595
|
field: containerField,
|
|
@@ -14637,31 +14735,31 @@ const BUILT_IN_FIELDS = [
|
|
|
14637
14735
|
// `row` is a virtual field type: it maps to ContainerFieldComponent, and
|
|
14638
14736
|
// the rowFieldMapper injects a `{ type: 'row' }` wrapper so the container
|
|
14639
14737
|
// renders the flex/grid layout via RowWrapperComponent.
|
|
14640
|
-
loadComponent: () => import('./ng-forge-dynamic-forms-container-field.component-
|
|
14738
|
+
loadComponent: () => import('./ng-forge-dynamic-forms-container-field.component-ByKgY4-b.mjs'),
|
|
14641
14739
|
mapper: rowFieldMapper,
|
|
14642
14740
|
valueHandling: 'flatten',
|
|
14643
14741
|
},
|
|
14644
14742
|
{
|
|
14645
14743
|
name: 'group',
|
|
14646
|
-
loadComponent: () => import('./ng-forge-dynamic-forms-group-field.component-
|
|
14744
|
+
loadComponent: () => import('./ng-forge-dynamic-forms-group-field.component-clrQsUyY.mjs'),
|
|
14647
14745
|
mapper: groupFieldMapper,
|
|
14648
14746
|
valueHandling: 'include',
|
|
14649
14747
|
},
|
|
14650
14748
|
{
|
|
14651
14749
|
name: 'array',
|
|
14652
|
-
loadComponent: () => import('./ng-forge-dynamic-forms-array-field.component-
|
|
14750
|
+
loadComponent: () => import('./ng-forge-dynamic-forms-array-field.component-DWhbvZUO.mjs'),
|
|
14653
14751
|
mapper: arrayFieldMapper,
|
|
14654
14752
|
valueHandling: 'include',
|
|
14655
14753
|
},
|
|
14656
14754
|
{
|
|
14657
14755
|
name: 'page',
|
|
14658
|
-
loadComponent: () => import('./ng-forge-dynamic-forms-page-field.component-
|
|
14756
|
+
loadComponent: () => import('./ng-forge-dynamic-forms-page-field.component-XtyjFlJi.mjs'),
|
|
14659
14757
|
mapper: pageFieldMapper,
|
|
14660
14758
|
valueHandling: 'flatten',
|
|
14661
14759
|
},
|
|
14662
14760
|
{
|
|
14663
14761
|
name: 'text',
|
|
14664
|
-
loadComponent: () => import('./ng-forge-dynamic-forms-text-field.component-
|
|
14762
|
+
loadComponent: () => import('./ng-forge-dynamic-forms-text-field.component-CJsRWH2r.mjs'),
|
|
14665
14763
|
mapper: textFieldMapper,
|
|
14666
14764
|
valueHandling: 'exclude',
|
|
14667
14765
|
...DISPLAY_FIELD_TYPES_BASE,
|
|
@@ -14673,7 +14771,7 @@ const BUILT_IN_FIELDS = [
|
|
|
14673
14771
|
},
|
|
14674
14772
|
{
|
|
14675
14773
|
name: 'container',
|
|
14676
|
-
loadComponent: () => import('./ng-forge-dynamic-forms-container-field.component-
|
|
14774
|
+
loadComponent: () => import('./ng-forge-dynamic-forms-container-field.component-ByKgY4-b.mjs'),
|
|
14677
14775
|
mapper: containerFieldMapper,
|
|
14678
14776
|
valueHandling: 'flatten',
|
|
14679
14777
|
},
|
|
@@ -14681,7 +14779,7 @@ const BUILT_IN_FIELDS = [
|
|
|
14681
14779
|
const BUILT_IN_WRAPPERS = [
|
|
14682
14780
|
{
|
|
14683
14781
|
wrapperName: 'css',
|
|
14684
|
-
loadComponent: () => import('./ng-forge-dynamic-forms-css-wrapper.component-
|
|
14782
|
+
loadComponent: () => import('./ng-forge-dynamic-forms-css-wrapper.component-ClFdxZV4.mjs'),
|
|
14685
14783
|
},
|
|
14686
14784
|
{
|
|
14687
14785
|
wrapperName: 'row',
|
|
@@ -15714,4 +15812,4 @@ function extractErrorParams(error) {
|
|
|
15714
15812
|
*/
|
|
15715
15813
|
|
|
15716
15814
|
export { SubmitEvent as $, ARRAY_CONTEXT as A, BUILT_IN_FIELDS as B, CONTAINER_FIELD_PROCESSORS as C, DynamicFormLogger as D, EventBus as E, FIELD_SIGNAL_CONTEXT as F, DEFAULT_VALIDATION_MESSAGES as G, DynamicForm as H, DynamicFormError as I, EventDispatcher as J, FIELD_REGISTRY as K, FORM_OPTIONS as L, FormClearEvent as M, FormResetEvent as N, INITIALIZATION_TIMEOUT_MS as O, InsertArrayItemEvent as P, MoveArrayItemEvent as Q, RootFormRegistryService as R, NextPageEvent as S, NoopLogger as T, PageChangeEvent as U, PopArrayItemEvent as V, WRAPPER_AUTO_ASSOCIATIONS as W, PrependArrayItemEvent as X, PreviousPageEvent as Y, RemoveAtIndexEvent as Z, ShiftArrayItemEvent as _, DEFAULT_WRAPPERS as a, WRAPPER_REGISTRY as a0, applyMetaToElement as a1, applyValidator as a2, applyValidators as a3, arrayEvent as a4, arrayFieldMapper as a5, baseFieldMapper as a6, buildBaseInputs as a7, containerFieldMapper as a8, createField as a9, resolveNextButtonDisabled as aA, resolveNonFieldDisabled as aB, resolveNonFieldHidden as aC, resolveSubmitButtonDisabled as aD, resolveTokens as aE, rowFieldMapper as aF, textFieldMapper as aG, toReadonlyFieldTree as aH, withEventFormValue as aI, withLoggerConfig as aJ, withPreviousValue as aK, withValueExclusionDefaults as aL, wrapperProps as aM, createWrappers as aa, evaluateNonFieldDisabled as ab, evaluateNonFieldHidden as ac, field as ad, formConfig as ae, getArrayLength as af, groupFieldMapper as ag, hasFormValue as ah, interpolateParams as ai, isArrayField as aj, isCheckedField as ak, isContainerField as al, isContainerTypedField as am, isDisplayOnlyField as an, isFormStateCondition as ao, isLeafField as ap, isPageField as aq, isRowField as ar, isSimplifiedArrayField as as, isValueBearingField as at, isValueField as au, isWrapperTypeDefinition as av, isWrappersBundle as aw, omit as ax, pageFieldMapper as ay, provideDynamicForm as az, isSameWrapperChain as b, createFieldResolutionPipe as c, derivedFromDeferred as d, createWrapperChainController as e, DfFieldOutlet as f, isEqual as g, createSchemaFromFields as h, injectFieldRegistry as i, createRenderReadySignal as j, ARRAY_TEMPLATE_REGISTRY as k, ARRAY_ITEM_ID_GENERATOR as l, mapFieldToInputs as m, getNormalizedArrayMetadata as n, getFieldValueHandling as o, getFieldDefaultValue as p, isGroupField as q, resolveWrappers as r, createArrayItemIdGenerator as s, ComponentInitializedEvent as t, DynamicTextPipe as u, validatePageNesting as v, dynamicTextToObservable as w, AppendArrayItemEvent as x, ConsoleLogger as y, DEFAULT_PROPS as z };
|
|
15717
|
-
//# sourceMappingURL=ng-forge-dynamic-forms-ng-forge-dynamic-forms-
|
|
15815
|
+
//# sourceMappingURL=ng-forge-dynamic-forms-ng-forge-dynamic-forms-Bw9Bt0Ev.mjs.map
|