Skip to content

Commit

Permalink
fix(autocomplete): input focus behavior on value change (#2852)
Browse files Browse the repository at this point in the history
BREAKING CHANGE:
Removed `focusInputOnValueChange` from `NbAutocompleteDirective`. Now autocomplete focuses input only when value is selected by option click or keyboard selection.
  • Loading branch information
katebatura committed Nov 15, 2021
1 parent 9a5a0f6 commit a86da11
Showing 1 changed file with 43 additions and 64 deletions.
107 changes: 43 additions & 64 deletions src/framework/theme/components/autocomplete/autocomplete.directive.ts
Expand Up @@ -39,7 +39,6 @@ import {
} from '../cdk/a11y/descendant-key-manager';
import { NbScrollStrategies } from '../cdk/adapter/block-scroll-strategy-adapter';
import { NbOptionComponent } from '../option/option.component';
import { convertToBoolProperty } from '../helpers';
import { NbAutocompleteComponent } from './autocomplete.component';

/**
Expand Down Expand Up @@ -81,14 +80,15 @@ import { NbAutocompleteComponent } from './autocomplete.component';
* */
@Directive({
selector: 'input[nbAutocomplete]',
providers: [{
provide: NG_VALUE_ACCESSOR,
useExisting: forwardRef(() => NbAutocompleteDirective),
multi: true,
}],
providers: [
{
provide: NG_VALUE_ACCESSOR,
useExisting: forwardRef(() => NbAutocompleteDirective),
multi: true,
},
],
})
export class NbAutocompleteDirective<T> implements OnDestroy, AfterViewInit, ControlValueAccessor {

/**
* NbAutocompleteComponent instance passed via input.
* */
Expand Down Expand Up @@ -142,18 +142,6 @@ export class NbAutocompleteDirective<T> implements OnDestroy, AfterViewInit, Con
**/
@Input() overlayOffset: number = 8;

/**
* Determines if the input will be focused when the control value is changed
* */
@Input()
get focusInputOnValueChange(): boolean {
return this._focusInputOnValueChange;
}
set focusInputOnValueChange(value: boolean) {
this._focusInputOnValueChange = convertToBoolProperty(value);
}
protected _focusInputOnValueChange: boolean = true;

/**
* Determines options overlay scroll strategy.
**/
Expand Down Expand Up @@ -192,7 +180,7 @@ export class NbAutocompleteDirective<T> implements OnDestroy, AfterViewInit, Con

@HostBinding('attr.aria-activedescendant')
get ariaActiveDescendant() {
return (this.isOpen && this.keyManager.activeItem) ? this.keyManager.activeItem.id : null;
return this.isOpen && this.keyManager.activeItem ? this.keyManager.activeItem.id : null;
}

constructor(
Expand All @@ -211,7 +199,6 @@ export class NbAutocompleteDirective<T> implements OnDestroy, AfterViewInit, Con
}

ngOnDestroy() {

if (this.triggerStrategy) {
this.triggerStrategy.destroy();
}
Expand Down Expand Up @@ -289,20 +276,18 @@ export class NbAutocompleteDirective<T> implements OnDestroy, AfterViewInit, Con
tap(() => this.setActiveItem()),
startWith(this.autocomplete.options),
switchMap((options: QueryList<NbOptionComponent<T>>) => {
return merge(...options.map(option => option.click));
return merge(...options.map((option) => option.click));
}),
takeUntil(this.destroy$),
)
.subscribe((clickedOption: NbOptionComponent<T>) => this.handleInputValueUpdate(clickedOption.value));
.subscribe((clickedOption: NbOptionComponent<T>) => this.handleInputValueUpdate(clickedOption.value, true));
}

protected subscribeOnPositionChange() {
this.positionStrategy.positionChange
.pipe(takeUntil(this.destroy$))
.subscribe((position: NbPosition) => {
this.autocomplete.overlayPosition = position;
this.cd.detectChanges();
});
this.positionStrategy.positionChange.pipe(takeUntil(this.destroy$)).subscribe((position: NbPosition) => {
this.autocomplete.overlayPosition = position;
this.cd.detectChanges();
});
}

protected getActiveItem(): NbOptionComponent<T> {
Expand All @@ -319,32 +304,31 @@ export class NbAutocompleteDirective<T> implements OnDestroy, AfterViewInit, Con
}

protected getContainer() {
return this.overlayRef && this.isOpen && <ComponentRef<any>> {
location: {
nativeElement: this.overlayRef.overlayElement,
},
};
}

protected handleInputValueUpdate(value: T) {
return (
this.overlayRef &&
this.isOpen &&
<ComponentRef<any>>{
location: {
nativeElement: this.overlayRef.overlayElement,
},
}
);
}

protected handleInputValueUpdate(value: T, focusInput: boolean = false) {
this.setHostInputValue(value ?? '');
this._onChange(value);
if (this.focusInputOnValueChange) {
if (focusInput) {
this.hostRef.nativeElement.focus();
}
this.autocomplete.emitSelected(value);
this.hide();
}

protected subscribeOnTriggers() {
this.triggerStrategy.show$.pipe(filter(() => this.isClosed)).subscribe(() => this.show());

this.triggerStrategy.show$
.pipe(filter(() => this.isClosed))
.subscribe(() => this.show());

this.triggerStrategy.hide$
.pipe(filter(() => this.isOpen))
.subscribe(() => this.hide());
this.triggerStrategy.hide$.pipe(filter(() => this.isOpen)).subscribe(() => this.hide());
}

protected createTriggerStrategy(): NbTriggerStrategy {
Expand All @@ -356,8 +340,7 @@ export class NbAutocompleteDirective<T> implements OnDestroy, AfterViewInit, Con
}

protected createKeyManager(): void {
this.keyManager = this.activeDescendantKeyManagerFactory
.create(this.autocomplete.options);
this.keyManager = this.activeDescendantKeyManagerFactory.create(this.autocomplete.options);
}

protected setHostInputValue(value) {
Expand All @@ -373,29 +356,25 @@ export class NbAutocompleteDirective<T> implements OnDestroy, AfterViewInit, Con
}

protected subscribeOnOverlayKeys(): void {
this.overlayRef.keydownEvents()
.pipe(
takeUntil(this.destroy$),
)
this.overlayRef
.keydownEvents()
.pipe(takeUntil(this.destroy$))
.subscribe((event: KeyboardEvent) => {
if (event.keyCode === ESCAPE && this.isOpen) {
event.preventDefault();
this.hostRef.nativeElement.focus();
this.hide();

} else if (event.keyCode === ENTER) {
event.preventDefault();
const activeItem = this.getActiveItem();
if (!activeItem) {
return;
}
this.handleInputValueUpdate(activeItem.value);

this.handleInputValueUpdate(activeItem.value, true);
} else {
this.keyManager.onKeydown(event);
}
});

}

protected setActiveItem() {
Expand All @@ -418,8 +397,11 @@ export class NbAutocompleteDirective<T> implements OnDestroy, AfterViewInit, Con

protected createOverlay() {
const scrollStrategy = this.createScrollStrategy();
this.overlayRef = this.overlay.create(
{ positionStrategy: this.positionStrategy, scrollStrategy, panelClass: this.autocomplete.optionsPanelClass });
this.overlayRef = this.overlay.create({
positionStrategy: this.positionStrategy,
scrollStrategy,
panelClass: this.autocomplete.optionsPanelClass,
});
}

protected initOverlay() {
Expand All @@ -434,13 +416,10 @@ export class NbAutocompleteDirective<T> implements OnDestroy, AfterViewInit, Con
}

protected checkOverlayVisibility() {
this.autocomplete.options.changes
.pipe(
takeUntil(this.destroy$),
).subscribe(() => {
if (!this.autocomplete.options.length) {
this.hide();
}
this.autocomplete.options.changes.pipe(takeUntil(this.destroy$)).subscribe(() => {
if (!this.autocomplete.options.length) {
this.hide();
}
});
}

Expand Down

0 comments on commit a86da11

Please sign in to comment.