@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.
@@ -9301,9 +9301,9 @@ function flattenControls(input) {
9301
9301
  class MetaFormV2Component {
9302
9302
  form;
9303
9303
  config;
9304
- /** page-level readOnly (parent toggles) */
9304
+ /** Page-level readOnly (parent toggles) */
9305
9305
  readOnly = false;
9306
- /** optional layout props */
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
- // ako se ništa bitno nije promenilo — izađi
9327
+ // If nothing relevant changed, exit
9328
9328
  if (!configChanged && !formChanged && !metaChanged && !enteringEdit)
9329
9329
  return;
9330
- // cleanup dependencies (samo kad config/meta menjaš)
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
- // ensure controls + sync validators
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
- // patch initial values (no emit)
9338
+ // Patch initial values (no emit)
9339
9339
  this.form.patchValue(initial, { emitEvent: false });
9340
9340
  this.form.updateValueAndValidity({ emitEvent: false });
9341
- // ✅ init expanded only when STRUCTURE changed (metaChanged)
9342
- // otvori sve što je collapsed:false
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
- // register submit-only validators
9357
+ // Register submit-only validators (safe even if empty)
9356
9358
  this.submitValidator.register(this.form, this.config.submitValidators);
9357
- // bind dependencies (samo kad config/meta menjaš)
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
- // ✅ entering edit:
9370
- // 1) odmah pokaži sync greške
9371
- // 2) auto-expand invalid groups (ne zatvaraj postojeće)
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.form.markAllAsTouched();
9375
- // da status/value promene “prođu” do polja
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
- // ---- template helpers ----
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
- groupHasInvalid(g) {
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)?.invalid);
9482
+ return keys.some((k) => this.isVisibleInvalid(this.form.get(k)));
9423
9483
  }
9424
- /** open invalid groups but KEEP already opened ones */
9425
- expandInvalidGroupsUnion() {
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.groupHasInvalid(g))
9490
+ .filter((g) => this.groupHasVisibleInvalid(g))
9431
9491
  .map((g) => this.panelValue(g))
9432
9492
  .filter(Boolean);
9433
9493
  if (!invalidIds.length)