@myrmidon/gve-core 6.1.0 → 6.1.1
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.
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import * as i0 from '@angular/core';
|
|
2
|
-
import { input, output, ChangeDetectionStrategy, Component, inject, DestroyRef, signal, computed, effect, viewChild, model,
|
|
2
|
+
import { input, output, ChangeDetectionStrategy, Component, inject, DestroyRef, signal, computed, effect, viewChild, model, untracked, Injectable, Optional, Inject, CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';
|
|
3
3
|
import * as i1 from '@angular/material/core';
|
|
4
4
|
import { MatRippleModule } from '@angular/material/core';
|
|
5
5
|
import * as i2$1 from '@myrmidon/ngx-tools';
|
|
@@ -16,8 +16,8 @@ import * as i5 from '@angular/material/input';
|
|
|
16
16
|
import { MatInputModule } from '@angular/material/input';
|
|
17
17
|
import * as i6 from '@angular/material/tooltip';
|
|
18
18
|
import { MatTooltipModule, MatTooltip } from '@angular/material/tooltip';
|
|
19
|
-
import { Subject, debounceTime, distinctUntilChanged,
|
|
20
|
-
import { takeUntilDestroyed
|
|
19
|
+
import { Subject, debounceTime, distinctUntilChanged, catchError, BehaviorSubject } from 'rxjs';
|
|
20
|
+
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
|
|
21
21
|
import * as i2$3 from '@angular/material/snack-bar';
|
|
22
22
|
import { MatSnackBar, MatSnackBarModule } from '@angular/material/snack-bar';
|
|
23
23
|
import * as i2$2 from '@angular/cdk/clipboard';
|
|
@@ -669,7 +669,28 @@ class FeatureEditorComponent {
|
|
|
669
669
|
* Event emitted when the user cancels the feature editing.
|
|
670
670
|
*/
|
|
671
671
|
this.featureCancel = output();
|
|
672
|
+
/**
|
|
673
|
+
* The allowed value IDs for the currently selected feature name.
|
|
674
|
+
* Set synchronously in updateForm (on feature load) and in the
|
|
675
|
+
* name.valueChanges subscription (on user interaction).
|
|
676
|
+
*/
|
|
672
677
|
this.nameIds = signal(undefined, ...(ngDevMode ? [{ debugName: "nameIds" }] : []));
|
|
678
|
+
/**
|
|
679
|
+
* Tracks the current feature name. Updated synchronously in updateForm
|
|
680
|
+
* and in the name.valueChanges subscription so that isMultiValued stays
|
|
681
|
+
* in sync regardless of whether the change was programmatic or user-driven.
|
|
682
|
+
*/
|
|
683
|
+
this._currentName = signal('', ...(ngDevMode ? [{ debugName: "_currentName" }] : []));
|
|
684
|
+
/**
|
|
685
|
+
* Computed signal that determines if the current feature is multi-valued.
|
|
686
|
+
* Automatically updates when either _currentName or multiValuedFeatureIds
|
|
687
|
+
* changes.
|
|
688
|
+
*/
|
|
689
|
+
this.isMultiValued = computed(() => {
|
|
690
|
+
const ids = this.multiValuedFeatureIds();
|
|
691
|
+
const name = this._currentName();
|
|
692
|
+
return !!ids && !!name && ids.includes(name);
|
|
693
|
+
}, ...(ngDevMode ? [{ debugName: "isMultiValued" }] : []));
|
|
673
694
|
this.name = formBuilder.control('', {
|
|
674
695
|
validators: [Validators.required, Validators.maxLength(50)],
|
|
675
696
|
nonNullable: true,
|
|
@@ -696,41 +717,29 @@ class FeatureEditorComponent {
|
|
|
696
717
|
isGlobal: this.isGlobal,
|
|
697
718
|
isShortLived: this.isShortLived,
|
|
698
719
|
});
|
|
699
|
-
//
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
});
|
|
703
|
-
// Computed signal that automatically tracks dependencies and triggers
|
|
704
|
-
// change detection when either name or multiValuedFeatureIds changes
|
|
705
|
-
this.isMultiValued = computed(() => {
|
|
706
|
-
const ids = this.multiValuedFeatureIds();
|
|
707
|
-
const name = this.nameSignal();
|
|
708
|
-
return !!ids && !!name && ids.includes(name);
|
|
709
|
-
}, ...(ngDevMode ? [{ debugName: "isMultiValued" }] : []));
|
|
710
|
-
// when feature changes, update the form
|
|
720
|
+
// When the feature input changes, repopulate the form atomically.
|
|
721
|
+
// featValues is read via untracked() so changes to it do not retrigger
|
|
722
|
+
// this effect and accidentally overwrite in-progress user edits.
|
|
711
723
|
effect(() => {
|
|
712
724
|
this.updateForm(this.feature());
|
|
713
|
-
setTimeout(() =>
|
|
714
|
-
this.nameControl()?.nativeElement?.focus();
|
|
715
|
-
this.nameIds.set(this.getLabeledIdsFor(this.name.value, this.featValues()));
|
|
716
|
-
}, 500);
|
|
717
|
-
});
|
|
718
|
-
// diagnostic logging for multiValuedFeatureIds
|
|
719
|
-
effect(() => {
|
|
720
|
-
console.log('[FeatureEditor] multiValuedFeatureIds:', this.multiValuedFeatureIds(), 'isMultiValued:', this.isMultiValued());
|
|
725
|
+
setTimeout(() => this.nameControl()?.nativeElement?.focus(), 0);
|
|
721
726
|
});
|
|
722
727
|
}
|
|
723
728
|
ngOnInit() {
|
|
724
|
-
//
|
|
729
|
+
// Respond to user-initiated name changes only.
|
|
730
|
+
// updateForm uses { emitEvent: false } so programmatic loads never
|
|
731
|
+
// reach this subscription, eliminating the race that caused value.reset()
|
|
732
|
+
// to fire after a 300 ms debounce and wipe the just-loaded value.
|
|
725
733
|
this._sub = this.name.valueChanges
|
|
726
734
|
.pipe(debounceTime(300), distinctUntilChanged())
|
|
727
735
|
.subscribe((name) => {
|
|
728
|
-
|
|
736
|
+
this._currentName.set(name);
|
|
737
|
+
this.nameIds.set(this.getLabeledIdsFor(name, this.featValues()));
|
|
738
|
+
// Reset value only when preset names are in use; for free-text names
|
|
739
|
+
// the value is independent and must not be cleared.
|
|
740
|
+
if (this.featNames()?.length) {
|
|
729
741
|
this.value.reset();
|
|
730
|
-
this.nameIds.set(this.getLabeledIdsFor(name, this.featValues()));
|
|
731
742
|
}
|
|
732
|
-
// Note: isMultiValued is now a computed signal that automatically
|
|
733
|
-
// updates when nameSignal or multiValuedFeatureIds changes
|
|
734
743
|
});
|
|
735
744
|
}
|
|
736
745
|
ngOnDestroy() {
|
|
@@ -740,26 +749,32 @@ class FeatureEditorComponent {
|
|
|
740
749
|
if (!id) {
|
|
741
750
|
return undefined;
|
|
742
751
|
}
|
|
743
|
-
console.log('getLabeledIdsFor', id, featValues);
|
|
744
752
|
return featValues ? featValues[id] : undefined;
|
|
745
753
|
}
|
|
746
754
|
updateForm(feature) {
|
|
747
755
|
if (!feature) {
|
|
756
|
+
this._currentName.set('');
|
|
757
|
+
this.nameIds.set(undefined);
|
|
748
758
|
this.form.reset();
|
|
749
|
-
// Note: isMultiValued is a computed signal - it will automatically
|
|
750
|
-
// update to false when name is reset to empty string
|
|
751
759
|
}
|
|
752
760
|
else {
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
761
|
+
// Read featValues outside the reactive context so that changes to
|
|
762
|
+
// featValues alone do not re-trigger the parent effect and overwrite
|
|
763
|
+
// any in-progress user edits.
|
|
764
|
+
const featValues = untracked(() => this.featValues());
|
|
765
|
+
// Update derived signals first so the template can resolve the correct
|
|
766
|
+
// control type (select vs text) before Angular renders.
|
|
767
|
+
this._currentName.set(feature.name);
|
|
768
|
+
this.nameIds.set(this.getLabeledIdsFor(feature.name, featValues));
|
|
769
|
+
// Use emitEvent: false to suppress valueChanges events.
|
|
770
|
+
// This prevents the name.valueChanges subscription (which resets the
|
|
771
|
+
// value control) from firing during a programmatic load.
|
|
772
|
+
this.name.setValue(feature.name, { emitEvent: false });
|
|
773
|
+
this.value.setValue(feature.value, { emitEvent: false });
|
|
756
774
|
this.setPolicy.setValue(feature.setPolicy || FeatureSetPolicy.multiple);
|
|
757
775
|
this.isNegated.setValue(feature.isNegated || false);
|
|
758
776
|
this.isGlobal.setValue(feature.isGlobal || false);
|
|
759
777
|
this.isShortLived.setValue(feature.isShortLived || false);
|
|
760
|
-
// Note: isMultiValued is a computed signal - it will automatically
|
|
761
|
-
// update based on the new name value and multiValuedFeatureIds
|
|
762
|
-
this._frozen = false;
|
|
763
778
|
}
|
|
764
779
|
}
|
|
765
780
|
addValueToComposite() {
|
|
@@ -887,10 +902,6 @@ class FeatureSetEditorComponent {
|
|
|
887
902
|
effect(() => {
|
|
888
903
|
this.applyFeatureFilter(this.features(), this.filter.value);
|
|
889
904
|
});
|
|
890
|
-
// diagnostic logging for multiValuedFeatureIds
|
|
891
|
-
effect(() => {
|
|
892
|
-
console.log('[FeatureSetEditor] multiValuedFeatureIds:', this.multiValuedFeatureIds());
|
|
893
|
-
});
|
|
894
905
|
}
|
|
895
906
|
applyFeatureFilter(features, name) {
|
|
896
907
|
this.filteredFeatures.set(features.filter((feature) => {
|