@fundamental-ngx/cdk 0.63.0-rc.33 → 0.63.0-rc.34
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.
|
@@ -652,6 +652,13 @@ class AutoCompleteDirective {
|
|
|
652
652
|
this._stopKeys = [BACKSPACE, DELETE, ESCAPE];
|
|
653
653
|
/** @hidden */
|
|
654
654
|
this._isComposing = false;
|
|
655
|
+
/**
|
|
656
|
+
* Tracks the intended user-typed value, derived from native `input` events.
|
|
657
|
+
* When Angular's NgModel re-pushes a stale value (e.g. after Ctrl+A → Delete),
|
|
658
|
+
* the DOM may contain the old model value when the user types the next character.
|
|
659
|
+
* We reconstruct the intended value using the cursor position and typed character.
|
|
660
|
+
*/
|
|
661
|
+
this._lastInputEventValue = null;
|
|
655
662
|
/** @hidden */
|
|
656
663
|
this._elementRef = inject(ElementRef);
|
|
657
664
|
/** @hidden */
|
|
@@ -667,6 +674,25 @@ class AutoCompleteDirective {
|
|
|
667
674
|
const keyupEvent = fromEvent(this._elementRef.nativeElement, 'keyup');
|
|
668
675
|
const compositionStartEvent = fromEvent(this._elementRef.nativeElement, 'compositionstart');
|
|
669
676
|
const compositionEndEvent = fromEvent(this._elementRef.nativeElement, 'compositionend');
|
|
677
|
+
// Track the intended user-typed value. Uses the cursor position and typed character
|
|
678
|
+
// to reconstruct the value the user intended, independent of any NgModel re-push.
|
|
679
|
+
fromEvent(this._elementRef.nativeElement, 'input')
|
|
680
|
+
.pipe(takeUntilDestroyed(this._destroyRef))
|
|
681
|
+
.subscribe((evt) => {
|
|
682
|
+
const el = this._elementRef.nativeElement;
|
|
683
|
+
const cursorPos = el.selectionStart ?? el.value.length;
|
|
684
|
+
if (evt.inputType === 'insertText' && evt.data) {
|
|
685
|
+
// Cursor is after the inserted char. Intended value = previously tracked
|
|
686
|
+
// prefix + new char. Handles the case where NgModel re-pushed a stale
|
|
687
|
+
// value before this keystroke (e.g. "Apple" after Ctrl+A→Delete→"B").
|
|
688
|
+
const prev = this._lastInputEventValue ?? '';
|
|
689
|
+
this._lastInputEventValue = prev + evt.data;
|
|
690
|
+
}
|
|
691
|
+
else {
|
|
692
|
+
// Deletion, paste, or other input — use current DOM value at cursor.
|
|
693
|
+
this._lastInputEventValue = el.value.substring(0, cursorPos);
|
|
694
|
+
}
|
|
695
|
+
});
|
|
670
696
|
keyupEvent.pipe(takeUntilDestroyed()).subscribe((evt) => this._handleKeyboardEvent(evt));
|
|
671
697
|
compositionStartEvent.pipe(takeUntilDestroyed(this._destroyRef)).subscribe(() => {
|
|
672
698
|
this._isComposing = true;
|
|
@@ -680,7 +706,10 @@ class AutoCompleteDirective {
|
|
|
680
706
|
_handleKeyboardEvent(event) {
|
|
681
707
|
if (this.enable && !this._isComposing) {
|
|
682
708
|
if (KeyUtil.isKeyCode(event, this._stopKeys)) {
|
|
683
|
-
this._elementRef.nativeElement
|
|
709
|
+
const el = this._elementRef.nativeElement;
|
|
710
|
+
if (el.selectionStart !== el.selectionEnd) {
|
|
711
|
+
el.value = this.inputText;
|
|
712
|
+
}
|
|
684
713
|
}
|
|
685
714
|
else if (KeyUtil.isKeyCode(event, this._completeKeys)) {
|
|
686
715
|
this._sendCompleteEvent(true);
|
|
@@ -689,26 +718,38 @@ class AutoCompleteDirective {
|
|
|
689
718
|
else if (KeyUtil.isKeyCode(event, this._fillKeys)) {
|
|
690
719
|
this._sendCompleteEvent(false);
|
|
691
720
|
}
|
|
692
|
-
else if (!this._isControlKey(event)
|
|
693
|
-
const
|
|
721
|
+
else if (!this._isControlKey(event)) {
|
|
722
|
+
const el = this._elementRef.nativeElement;
|
|
723
|
+
const hasSelection = el.selectionStart !== el.selectionEnd;
|
|
724
|
+
// Use the value from the last native `input` event as the authoritative
|
|
725
|
+
// user-typed value. Angular's NgModel may have re-written el.value with a
|
|
726
|
+
// stale model value (e.g. after Ctrl+A → Delete), but the native `input`
|
|
727
|
+
// event only fires on actual user input — not on programmatic writes.
|
|
728
|
+
const currentNativeValue = this._lastInputEventValue !== null ? this._lastInputEventValue : el.value;
|
|
729
|
+
// After consuming, keep the current value so the next `input` event's
|
|
730
|
+
// `insertText` accumulation has the right prefix.
|
|
731
|
+
this._lastInputEventValue = currentNativeValue;
|
|
694
732
|
if (hasSelection) {
|
|
695
733
|
return;
|
|
696
734
|
}
|
|
697
|
-
|
|
698
|
-
if (this.inputText.length > currentNativeValue.length + 1) {
|
|
699
|
-
this.inputText = currentNativeValue;
|
|
735
|
+
if (!currentNativeValue) {
|
|
700
736
|
return;
|
|
701
737
|
}
|
|
738
|
+
const effectiveInputText = this.inputText.length > currentNativeValue.length + 1 ||
|
|
739
|
+
!this.inputText.toLocaleLowerCase().startsWith(currentNativeValue.toLocaleLowerCase())
|
|
740
|
+
? currentNativeValue
|
|
741
|
+
: this.inputText;
|
|
702
742
|
if (!this._triggerTypeAhead()) {
|
|
703
743
|
return;
|
|
704
744
|
}
|
|
705
745
|
this.oldValue = this.inputText;
|
|
706
|
-
const
|
|
746
|
+
const searchTerm = effectiveInputText || currentNativeValue;
|
|
747
|
+
const item = this.options.find((option) => this.matcher(this.displayFn(option).toLocaleLowerCase(), searchTerm.toLocaleLowerCase()));
|
|
707
748
|
if (item) {
|
|
708
749
|
const displayedValue = this.displayFn(item);
|
|
709
750
|
// Only autocomplete if the current native value matches the start of the found item
|
|
710
751
|
if (displayedValue.toLocaleLowerCase().startsWith(currentNativeValue.toLocaleLowerCase())) {
|
|
711
|
-
this._typeahead(displayedValue);
|
|
752
|
+
this._typeahead(displayedValue, currentNativeValue.length);
|
|
712
753
|
}
|
|
713
754
|
}
|
|
714
755
|
}
|
|
@@ -716,9 +757,9 @@ class AutoCompleteDirective {
|
|
|
716
757
|
this.lastKeyUpEvent = event;
|
|
717
758
|
}
|
|
718
759
|
/** @hidden */
|
|
719
|
-
_typeahead(displayedValue) {
|
|
760
|
+
_typeahead(displayedValue, currentInputLength) {
|
|
720
761
|
this._elementRef.nativeElement.value = displayedValue;
|
|
721
|
-
const selectionStartIndex =
|
|
762
|
+
const selectionStartIndex = currentInputLength;
|
|
722
763
|
this._elementRef.nativeElement.setSelectionRange(selectionStartIndex, displayedValue.length);
|
|
723
764
|
}
|
|
724
765
|
/** @hidden */
|