@fuentis/phoenix-ui 0.0.9-alpha.538 → 0.0.9-alpha.539
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/fuentis-phoenix-ui.mjs +86 -26
- package/fesm2022/fuentis-phoenix-ui.mjs.map +1 -1
- package/index.d.ts +23 -5
- package/package.json +1 -1
|
@@ -9301,9 +9301,9 @@ function flattenControls(input) {
|
|
|
9301
9301
|
class MetaFormV2Component {
|
|
9302
9302
|
form;
|
|
9303
9303
|
config;
|
|
9304
|
-
/**
|
|
9304
|
+
/** Page-level readOnly (parent toggles) */
|
|
9305
9305
|
readOnly = false;
|
|
9306
|
-
/**
|
|
9306
|
+
/** Optional layout props */
|
|
9307
9307
|
contentStyle = null;
|
|
9308
9308
|
contentClass = null;
|
|
9309
9309
|
fb = inject(FormBuilder);
|
|
@@ -9324,22 +9324,24 @@ class MetaFormV2Component {
|
|
|
9324
9324
|
const enteringEdit = !!changes['readOnly'] &&
|
|
9325
9325
|
changes['readOnly'].previousValue === true &&
|
|
9326
9326
|
changes['readOnly'].currentValue === false;
|
|
9327
|
-
//
|
|
9327
|
+
// If nothing relevant changed, exit
|
|
9328
9328
|
if (!configChanged && !formChanged && !metaChanged && !enteringEdit)
|
|
9329
9329
|
return;
|
|
9330
|
-
//
|
|
9330
|
+
// Cleanup dependencies ONLY when metadata/structure changes
|
|
9331
9331
|
if (configChanged || metaChanged) {
|
|
9332
9332
|
this.depCleanup?.();
|
|
9333
9333
|
this.depCleanup = undefined;
|
|
9334
9334
|
}
|
|
9335
|
-
//
|
|
9335
|
+
// Ensure controls exist + sync validators
|
|
9336
9336
|
const initial = this.config.initialValues ?? {};
|
|
9337
9337
|
ensureControlsV2(this.fb, this.form, flat, initial, { lang: this.translate.currentLang });
|
|
9338
|
-
//
|
|
9338
|
+
// Patch initial values (no emit)
|
|
9339
9339
|
this.form.patchValue(initial, { emitEvent: false });
|
|
9340
9340
|
this.form.updateValueAndValidity({ emitEvent: false });
|
|
9341
|
-
|
|
9342
|
-
|
|
9341
|
+
/**
|
|
9342
|
+
* Initialize accordion expanded panels ONLY when structure changed.
|
|
9343
|
+
* This must NOT run on readOnly toggle, otherwise you "reset" user-collapsed state.
|
|
9344
|
+
*/
|
|
9343
9345
|
if (metaChanged) {
|
|
9344
9346
|
if (this.isGrouped) {
|
|
9345
9347
|
const groups = this.groupedControls ?? [];
|
|
@@ -9352,9 +9354,9 @@ class MetaFormV2Component {
|
|
|
9352
9354
|
this.expandedGroupIds = [];
|
|
9353
9355
|
}
|
|
9354
9356
|
}
|
|
9355
|
-
//
|
|
9357
|
+
// Register submit-only validators (safe even if empty)
|
|
9356
9358
|
this.submitValidator.register(this.form, this.config.submitValidators);
|
|
9357
|
-
//
|
|
9359
|
+
// Bind dependencies ONLY when metadata/structure changes
|
|
9358
9360
|
if ((configChanged || metaChanged) && this.config.setupDependencies) {
|
|
9359
9361
|
const maybeCleanup = this.config.setupDependencies({
|
|
9360
9362
|
form: this.form,
|
|
@@ -9366,18 +9368,16 @@ class MetaFormV2Component {
|
|
|
9366
9368
|
if (typeof maybeCleanup === 'function')
|
|
9367
9369
|
this.depCleanup = maybeCleanup;
|
|
9368
9370
|
}
|
|
9369
|
-
|
|
9370
|
-
|
|
9371
|
-
|
|
9371
|
+
/**
|
|
9372
|
+
* Entering edit:
|
|
9373
|
+
* ✅ Show errors ONLY for controls that already have a value.
|
|
9374
|
+
* ❌ Do NOT trigger mandatory "required" messages for empty fields.
|
|
9375
|
+
* ✅ Expand groups that contain such "visible invalid" controls.
|
|
9376
|
+
*/
|
|
9372
9377
|
if (enteringEdit) {
|
|
9373
9378
|
queueMicrotask(() => {
|
|
9374
|
-
this.
|
|
9375
|
-
|
|
9376
|
-
Object.values(this.form.controls).forEach((c) => {
|
|
9377
|
-
c.updateValueAndValidity({ emitEvent: true });
|
|
9378
|
-
});
|
|
9379
|
-
this.form.updateValueAndValidity({ emitEvent: true });
|
|
9380
|
-
this.expandInvalidGroupsUnion();
|
|
9379
|
+
this.touchAndValidateOnlyFilledControls();
|
|
9380
|
+
this.expandVisibleInvalidGroupsUnion();
|
|
9381
9381
|
});
|
|
9382
9382
|
}
|
|
9383
9383
|
this.lastSignature = signature;
|
|
@@ -9393,7 +9393,7 @@ class MetaFormV2Component {
|
|
|
9393
9393
|
return [];
|
|
9394
9394
|
return [`${v}`];
|
|
9395
9395
|
}
|
|
9396
|
-
//
|
|
9396
|
+
// ---------------- template helpers ----------------
|
|
9397
9397
|
get hasControls() {
|
|
9398
9398
|
return Array.isArray(this.config?.controls) && this.config.controls.length > 0;
|
|
9399
9399
|
}
|
|
@@ -9415,19 +9415,79 @@ class MetaFormV2Component {
|
|
|
9415
9415
|
const id = g?.id;
|
|
9416
9416
|
return id === null || id === undefined ? '' : `${id}`;
|
|
9417
9417
|
}
|
|
9418
|
-
|
|
9418
|
+
// ---------------- core behavior ----------------
|
|
9419
|
+
/**
|
|
9420
|
+
* Mark & validate ONLY controls that already have a meaningful value.
|
|
9421
|
+
* This prevents "required" errors from showing immediately on CREATE dialogs.
|
|
9422
|
+
*/
|
|
9423
|
+
touchAndValidateOnlyFilledControls() {
|
|
9424
|
+
const controls = this.form?.controls ?? {};
|
|
9425
|
+
for (const [key, ctrl] of Object.entries(controls)) {
|
|
9426
|
+
if (!ctrl)
|
|
9427
|
+
continue;
|
|
9428
|
+
const value = ctrl.value;
|
|
9429
|
+
// Touch only if user already has a value (edit case) or prefilled values exist
|
|
9430
|
+
if (this.hasMeaningfulValue(value)) {
|
|
9431
|
+
ctrl.markAsTouched();
|
|
9432
|
+
ctrl.updateValueAndValidity({ emitEvent: true });
|
|
9433
|
+
}
|
|
9434
|
+
}
|
|
9435
|
+
// Keep form validity in sync (optional)
|
|
9436
|
+
this.form.updateValueAndValidity({ emitEvent: true });
|
|
9437
|
+
}
|
|
9438
|
+
/**
|
|
9439
|
+
* What counts as a "meaningful value"?
|
|
9440
|
+
* - non-empty strings
|
|
9441
|
+
* - numbers / booleans
|
|
9442
|
+
* - non-empty arrays
|
|
9443
|
+
* - objects with at least one key (or common "selected item" shapes)
|
|
9444
|
+
*/
|
|
9445
|
+
hasMeaningfulValue(v) {
|
|
9446
|
+
if (v === null || v === undefined)
|
|
9447
|
+
return false;
|
|
9448
|
+
if (typeof v === 'string')
|
|
9449
|
+
return v.trim().length > 0;
|
|
9450
|
+
if (typeof v === 'number')
|
|
9451
|
+
return true;
|
|
9452
|
+
if (typeof v === 'boolean')
|
|
9453
|
+
return true;
|
|
9454
|
+
if (Array.isArray(v))
|
|
9455
|
+
return v.length > 0;
|
|
9456
|
+
if (typeof v === 'object') {
|
|
9457
|
+
// common selection shapes: { key }, { uuid }, { id }, etc.
|
|
9458
|
+
if ('key' in v && v.key != null && `${v.key}`.trim() !== '')
|
|
9459
|
+
return true;
|
|
9460
|
+
if ('uuid' in v && v.uuid != null && `${v.uuid}`.trim() !== '')
|
|
9461
|
+
return true;
|
|
9462
|
+
if ('id' in v && v.id != null && `${v.id}`.trim() !== '')
|
|
9463
|
+
return true;
|
|
9464
|
+
// fallback: any own keys
|
|
9465
|
+
return Object.keys(v).length > 0;
|
|
9466
|
+
}
|
|
9467
|
+
return false;
|
|
9468
|
+
}
|
|
9469
|
+
/**
|
|
9470
|
+
* Visible invalid = invalid AND (touched OR dirty).
|
|
9471
|
+
* This aligns with the goal: don't expand groups due to empty required fields on create.
|
|
9472
|
+
*/
|
|
9473
|
+
isVisibleInvalid(ctrl) {
|
|
9474
|
+
if (!ctrl)
|
|
9475
|
+
return false;
|
|
9476
|
+
return ctrl.invalid && (ctrl.touched || ctrl.dirty);
|
|
9477
|
+
}
|
|
9478
|
+
groupHasVisibleInvalid(g) {
|
|
9419
9479
|
const keys = (g?.ctrl ?? [])
|
|
9420
9480
|
.map((f) => f?.configuration?.key)
|
|
9421
9481
|
.filter(Boolean);
|
|
9422
|
-
return keys.some((k) => this.form.get(k)
|
|
9482
|
+
return keys.some((k) => this.isVisibleInvalid(this.form.get(k)));
|
|
9423
9483
|
}
|
|
9424
|
-
/**
|
|
9425
|
-
|
|
9484
|
+
/** Expand invalid groups but KEEP already opened ones */
|
|
9485
|
+
expandVisibleInvalidGroupsUnion() {
|
|
9426
9486
|
if (!this.isGrouped)
|
|
9427
9487
|
return;
|
|
9428
9488
|
const groups = this.groupedControls ?? [];
|
|
9429
9489
|
const invalidIds = groups
|
|
9430
|
-
.filter((g) => this.
|
|
9490
|
+
.filter((g) => this.groupHasVisibleInvalid(g))
|
|
9431
9491
|
.map((g) => this.panelValue(g))
|
|
9432
9492
|
.filter(Boolean);
|
|
9433
9493
|
if (!invalidIds.length)
|