diff --git a/components/auto-complete/nz-autocomplete-optgroup.component.ts b/components/auto-complete/autocomplete-optgroup.component.ts similarity index 74% rename from components/auto-complete/nz-autocomplete-optgroup.component.ts rename to components/auto-complete/autocomplete-optgroup.component.ts index 6cd26ee8ad0..d9c67ac95cd 100644 --- a/components/auto-complete/nz-autocomplete-optgroup.component.ts +++ b/components/auto-complete/autocomplete-optgroup.component.ts @@ -14,11 +14,12 @@ import { ChangeDetectionStrategy, Component, Input, TemplateRef, ViewEncapsulati preserveWhitespaces: false, changeDetection: ChangeDetectionStrategy.OnPush, encapsulation: ViewEncapsulation.None, - templateUrl: './nz-autocomplete-optgroup.component.html', - host: { - role: 'group', - class: 'ant-select-dropdown-menu-item-group' - } + template: ` +
+ {{ nzLabel }} +
+ + ` }) export class NzAutocompleteOptgroupComponent { @Input() nzLabel: string | TemplateRef; diff --git a/components/auto-complete/nz-autocomplete-option.component.ts b/components/auto-complete/autocomplete-option.component.ts similarity index 74% rename from components/auto-complete/nz-autocomplete-option.component.ts rename to components/auto-complete/autocomplete-option.component.ts index 2b3757739e6..7e52e70878c 100644 --- a/components/auto-complete/nz-autocomplete-option.component.ts +++ b/components/auto-complete/autocomplete-option.component.ts @@ -13,12 +13,15 @@ import { ElementRef, EventEmitter, Input, + Optional, Output, ViewEncapsulation } from '@angular/core'; import { InputBoolean, scrollIntoView } from 'ng-zorro-antd/core'; +import { NzAutocompleteOptgroupComponent } from './autocomplete-optgroup.component'; + export class NzOptionSelectionChange { constructor(public source: NzAutocompleteOptionComponent, public isUserInput: boolean = false) {} } @@ -29,16 +32,22 @@ export class NzOptionSelectionChange { preserveWhitespaces: false, changeDetection: ChangeDetectionStrategy.OnPush, encapsulation: ViewEncapsulation.None, - templateUrl: './nz-autocomplete-option.component.html', + template: ` +
+ +
+ `, host: { role: 'menuitem', - class: 'ant-select-dropdown-menu-item', - '[class.ant-select-dropdown-menu-item-selected]': 'selected', - '[class.ant-select-dropdown-menu-item-active]': 'active', - '[class.ant-select-dropdown-menu-item-disabled]': 'nzDisabled', + class: 'ant-select-item ant-select-item-option', + '[class.ant-select-item-option-grouped]': 'nzAutocompleteOptgroupComponent', + '[class.ant-select-item-option-selected]': 'selected', + '[class.ant-select-item-option-active]': 'active', + '[class.ant-select-item-option-disabled]': 'nzDisabled', '[attr.aria-selected]': 'selected.toString()', '[attr.aria-disabled]': 'nzDisabled.toString()', '(click)': 'selectViaInteraction()', + '(mouseenter)': 'onMouseEnter()', '(mousedown)': '$event.preventDefault()' } }) @@ -48,11 +57,17 @@ export class NzAutocompleteOptionComponent { @Input() nzLabel: string; @Input() @InputBoolean() nzDisabled = false; @Output() readonly selectionChange = new EventEmitter(); + @Output() readonly mouseEntered = new EventEmitter(); active = false; selected = false; - constructor(private changeDetectorRef: ChangeDetectorRef, private element: ElementRef) {} + constructor( + private changeDetectorRef: ChangeDetectorRef, + private element: ElementRef, + @Optional() + public nzAutocompleteOptgroupComponent: NzAutocompleteOptgroupComponent + ) {} select(emit: boolean = true): void { this.selected = true; @@ -62,6 +77,10 @@ export class NzAutocompleteOptionComponent { } } + onMouseEnter(): void { + this.mouseEntered.emit(this); + } + deselect(): void { this.selected = false; this.changeDetectorRef.markForCheck(); diff --git a/components/auto-complete/nz-autocomplete-trigger.directive.ts b/components/auto-complete/autocomplete-trigger.directive.ts similarity index 98% rename from components/auto-complete/nz-autocomplete-trigger.directive.ts rename to components/auto-complete/autocomplete-trigger.directive.ts index 8f24f2758b0..1f70fc26379 100644 --- a/components/auto-complete/nz-autocomplete-trigger.directive.ts +++ b/components/auto-complete/autocomplete-trigger.directive.ts @@ -36,8 +36,8 @@ import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms'; import { fromEvent, merge, Subscription } from 'rxjs'; import { delay, distinct, map, take, tap } from 'rxjs/operators'; -import { NzAutocompleteOptionComponent } from './nz-autocomplete-option.component'; -import { NzAutocompleteComponent } from './nz-autocomplete.component'; +import { NzAutocompleteOptionComponent } from './autocomplete-option.component'; +import { NzAutocompleteComponent } from './autocomplete.component'; export const NZ_AUTOCOMPLETE_VALUE_ACCESSOR: ExistingProvider = { provide: NG_VALUE_ACCESSOR, diff --git a/components/auto-complete/nz-autocomplete.component.ts b/components/auto-complete/autocomplete.component.ts similarity index 74% rename from components/auto-complete/nz-autocomplete.component.ts rename to components/auto-complete/autocomplete.component.ts index 20b0cc06f50..f9bdeb56b0b 100644 --- a/components/auto-complete/nz-autocomplete.component.ts +++ b/components/auto-complete/autocomplete.component.ts @@ -32,7 +32,7 @@ import { filter, switchMap, take } from 'rxjs/operators'; import { CompareWith, InputBoolean, NzDropDownPosition, NzNoAnimationDirective, slideMotion } from 'ng-zorro-antd/core'; -import { NzAutocompleteOptionComponent, NzOptionSelectionChange } from './nz-autocomplete-option.component'; +import { NzAutocompleteOptionComponent, NzOptionSelectionChange } from './autocomplete-option.component'; export interface AutocompleteDataSourceItem { value: string; @@ -47,17 +47,37 @@ export type AutocompleteDataSource = AutocompleteDataSourceItem[] | string[] | n preserveWhitespaces: false, changeDetection: ChangeDetectionStrategy.OnPush, encapsulation: ViewEncapsulation.None, - templateUrl: './nz-autocomplete.component.html', + template: ` + +
+
+
+ +
+
+
+ + + + + {{ option }} + +
+ `, animations: [slideMotion], styles: [ ` - .ant-select-dropdown { - top: 100%; - left: 0; - position: relative; - width: 100%; - margin-top: 4px; - margin-bottom: 4px; + .ant-select-dropdown-hidden { + display: none; } ` ] @@ -70,7 +90,8 @@ export class NzAutocompleteComponent implements AfterContentInit, AfterViewInit, @Input() @InputBoolean() nzBackfill = false; @Input() compareWith: CompareWith = (o1, o2) => o1 === o2; @Input() nzDataSource: AutocompleteDataSource; - @Output() readonly selectionChange: EventEmitter = new EventEmitter(); + @Output() + readonly selectionChange: EventEmitter = new EventEmitter(); showPanel: boolean = true; isOpen: boolean = false; @@ -90,7 +111,8 @@ export class NzAutocompleteComponent implements AfterContentInit, AfterViewInit, } /** Provided by content */ - @ContentChildren(NzAutocompleteOptionComponent, { descendants: true }) fromContentOptions: QueryList; + @ContentChildren(NzAutocompleteOptionComponent, { descendants: true }) + fromContentOptions: QueryList; /** Provided by dataSource */ @ViewChildren(NzAutocompleteOptionComponent) fromDataSourceOptions: QueryList; @@ -101,6 +123,7 @@ export class NzAutocompleteComponent implements AfterContentInit, AfterViewInit, private activeItemIndex: number = -1; private selectionChangeSubscription = Subscription.EMPTY; + private optionMouseEnterSubscription = Subscription.EMPTY; private dataSourceChangeSubscription = Subscription.EMPTY; /** Options changes listener */ readonly optionSelectionChanges: Observable = defer(() => { @@ -112,6 +135,15 @@ export class NzAutocompleteComponent implements AfterContentInit, AfterViewInit, switchMap(() => this.optionSelectionChanges) ); }); + readonly optionMouseEnter: Observable = defer(() => { + if (this.options) { + return merge(...this.options.map(option => option.mouseEntered)); + } + return this.ngZone.onStable.asObservable().pipe( + take(1), + switchMap(() => this.optionMouseEnter) + ); + }); constructor( private changeDetectorRef: ChangeDetectorRef, @@ -134,6 +166,7 @@ export class NzAutocompleteComponent implements AfterContentInit, AfterViewInit, ngOnDestroy(): void { this.dataSourceChangeSubscription.unsubscribe(); this.selectionChangeSubscription.unsubscribe(); + this.optionMouseEnterSubscription.unsubscribe(); } setVisibility(): void { @@ -213,5 +246,13 @@ export class NzAutocompleteComponent implements AfterContentInit, AfterViewInit, this.clearSelectedOptions(event.source, true); this.selectionChange.emit(event.source); }); + + this.optionMouseEnterSubscription.unsubscribe(); + this.optionMouseEnterSubscription = this.optionMouseEnter.subscribe((event: NzAutocompleteOptionComponent) => { + event.setActiveStyles(); + this.activeItem = event; + this.activeItemIndex = this.getOptionIndex(this.activeItem.nzValue); + this.clearSelectedOptions(event); + }); } } diff --git a/components/auto-complete/nz-autocomplete.module.ts b/components/auto-complete/autocomplete.module.ts similarity index 73% rename from components/auto-complete/nz-autocomplete.module.ts rename to components/auto-complete/autocomplete.module.ts index 1ce4fc5fda5..ed43be311cb 100644 --- a/components/auto-complete/nz-autocomplete.module.ts +++ b/components/auto-complete/autocomplete.module.ts @@ -13,10 +13,10 @@ import { FormsModule } from '@angular/forms'; import { NzNoAnimationModule, NzOutletModule } from 'ng-zorro-antd/core'; -import { NzAutocompleteOptgroupComponent } from './nz-autocomplete-optgroup.component'; -import { NzAutocompleteOptionComponent } from './nz-autocomplete-option.component'; -import { NzAutocompleteTriggerDirective } from './nz-autocomplete-trigger.directive'; -import { NzAutocompleteComponent } from './nz-autocomplete.component'; +import { NzAutocompleteOptgroupComponent } from './autocomplete-optgroup.component'; +import { NzAutocompleteOptionComponent } from './autocomplete-option.component'; +import { NzAutocompleteTriggerDirective } from './autocomplete-trigger.directive'; +import { NzAutocompleteComponent } from './autocomplete.component'; @NgModule({ declarations: [NzAutocompleteComponent, NzAutocompleteOptionComponent, NzAutocompleteTriggerDirective, NzAutocompleteOptgroupComponent], diff --git a/components/auto-complete/nz-autocomplete.spec.ts b/components/auto-complete/autocomplete.spec.ts similarity index 94% rename from components/auto-complete/nz-autocomplete.spec.ts rename to components/auto-complete/autocomplete.spec.ts index 4b55fd52e5c..d167ad1b28d 100644 --- a/components/auto-complete/nz-autocomplete.spec.ts +++ b/components/auto-complete/autocomplete.spec.ts @@ -11,8 +11,8 @@ import { Subject } from 'rxjs'; import { createKeyboardEvent, dispatchFakeEvent, dispatchKeyboardEvent, MockNgZone, typeInElement } from 'ng-zorro-antd/core'; +import { getNzAutocompleteMissingPanelError } from './autocomplete-trigger.directive'; import { NzAutocompleteComponent, NzAutocompleteModule, NzAutocompleteOptionComponent, NzAutocompleteTriggerDirective } from './index'; -import { getNzAutocompleteMissingPanelError } from './nz-autocomplete-trigger.directive'; describe('auto-complete', () => { let overlayContainer: OverlayContainer; @@ -102,11 +102,10 @@ describe('auto-complete', () => { input.disabled = true; fixture.detectChanges(); - expect(trigger.panelOpen).toBe(false); dispatchFakeEvent(input, 'focusin'); flush(); - fixture.detectChanges(); + expect(trigger.panelOpen).toBe(false); })); @@ -243,16 +242,6 @@ describe('auto-complete', () => { TAB_EVENT = createKeyboardEvent('keydown', TAB); }); - it('should open the panel when the input is focused', () => { - expect(fixture.componentInstance.trigger.panelOpen).toBe(false); - - dispatchFakeEvent(input, 'focusin'); - fixture.detectChanges(); - - expect(fixture.componentInstance.trigger.panelOpen).toBe(true); - expect(overlayContainerElement.textContent).toContain('Burns Bay Road'); - }); - it('should have correct width when setting', () => { fixture.componentInstance.width = 500; fixture.detectChanges(); @@ -603,8 +592,8 @@ describe('auto-complete', () => { componentInstance.trigger.handleKeydown(DOWN_ARROW_EVENT); fixture.detectChanges(); - expect(optionEls[0].classList).not.toContain('ant-select-dropdown-menu-item-active'); - expect(optionEls[1].classList).toContain('ant-select-dropdown-menu-item-active'); + expect(optionEls[0].classList).not.toContain('ant-select-item-option-active'); + expect(optionEls[1].classList).toContain('ant-select-item-option-active'); }); it('should set the active item to the first option when DOWN key is pressed in last item', () => { @@ -616,8 +605,33 @@ describe('auto-complete', () => { [1, 2, 3].forEach(() => componentInstance.trigger.handleKeydown(DOWN_ARROW_EVENT)); fixture.detectChanges(); - expect(optionEls[1].classList).not.toContain('ant-select-dropdown-menu-item-active'); - expect(optionEls[0].classList).toContain('ant-select-dropdown-menu-item-active'); + expect(optionEls[1].classList).not.toContain('ant-select-item-option-active'); + expect(optionEls[0].classList).toContain('ant-select-item-option-active'); + }); + + it('should set the active item when mouse is enter', () => { + const componentInstance = fixture.componentInstance; + const optionEls = overlayContainerElement.querySelectorAll('nz-auto-option') as NodeListOf; + + expect(componentInstance.trigger.panelOpen).toBe(true); + + fixture.detectChanges(); + + dispatchFakeEvent(optionEls[1], 'mouseenter'); + + fixture.detectChanges(); + + expect(optionEls[0].classList).not.toContain('ant-select-item-option-active'); + expect(optionEls[1].classList).toContain('ant-select-item-option-active'); + expect(optionEls[2].classList).not.toContain('ant-select-item-option-active'); + + dispatchFakeEvent(optionEls[0], 'mouseenter'); + + fixture.detectChanges(); + + expect(optionEls[0].classList).toContain('ant-select-item-option-active'); + expect(optionEls[1].classList).not.toContain('ant-select-item-option-active'); + expect(optionEls[2].classList).not.toContain('ant-select-item-option-active'); }); it('should set the active item to the last option when UP key is pressed', () => { @@ -629,9 +643,9 @@ describe('auto-complete', () => { componentInstance.trigger.handleKeydown(UP_ARROW_EVENT); fixture.detectChanges(); - expect(optionEls[0].classList).not.toContain('ant-select-dropdown-menu-item-active'); - expect(optionEls[1].classList).not.toContain('ant-select-dropdown-menu-item-active'); - expect(optionEls[2].classList).toContain('ant-select-dropdown-menu-item-active'); + expect(optionEls[0].classList).not.toContain('ant-select-item-option-active'); + expect(optionEls[1].classList).not.toContain('ant-select-item-option-active'); + expect(optionEls[2].classList).toContain('ant-select-item-option-active'); }); it('should set the active item to the previous option when UP key is pressed', () => { @@ -643,9 +657,9 @@ describe('auto-complete', () => { [1, 2].forEach(() => componentInstance.trigger.handleKeydown(UP_ARROW_EVENT)); fixture.detectChanges(); - expect(optionEls[0].classList).not.toContain('ant-select-dropdown-menu-item-active'); - expect(optionEls[1].classList).toContain('ant-select-dropdown-menu-item-active'); - expect(optionEls[2].classList).not.toContain('ant-select-dropdown-menu-item-active'); + expect(optionEls[0].classList).not.toContain('ant-select-item-option-active'); + expect(optionEls[1].classList).toContain('ant-select-item-option-active'); + expect(optionEls[2].classList).not.toContain('ant-select-item-option-active'); }); it('should set the active item properly after filtering', () => { @@ -659,8 +673,8 @@ describe('auto-complete', () => { const optionEls = overlayContainerElement.querySelectorAll('nz-auto-option') as NodeListOf; - expect(optionEls[0].classList).not.toContain('ant-select-dropdown-menu-item-active'); - expect(optionEls[1].classList).toContain('ant-select-dropdown-menu-item-active'); + expect(optionEls[0].classList).not.toContain('ant-select-item-option-active'); + expect(optionEls[1].classList).toContain('ant-select-item-option-active'); expect(optionEls[1].innerText).toEqual('Wall Street'); }); diff --git a/components/auto-complete/nz-autocomplete-optgroup.component.html b/components/auto-complete/nz-autocomplete-optgroup.component.html deleted file mode 100644 index 01eaa19fa75..00000000000 --- a/components/auto-complete/nz-autocomplete-optgroup.component.html +++ /dev/null @@ -1,6 +0,0 @@ -
- {{ nzLabel }} -
-
    - -
diff --git a/components/auto-complete/nz-autocomplete-option.component.html b/components/auto-complete/nz-autocomplete-option.component.html deleted file mode 100644 index 6dbc7430638..00000000000 --- a/components/auto-complete/nz-autocomplete-option.component.html +++ /dev/null @@ -1 +0,0 @@ - diff --git a/components/auto-complete/nz-autocomplete.component.html b/components/auto-complete/nz-autocomplete.component.html deleted file mode 100644 index bb1098b9347..00000000000 --- a/components/auto-complete/nz-autocomplete.component.html +++ /dev/null @@ -1,32 +0,0 @@ - -
-
- -
-
- - - - - {{ - option - }} - -
diff --git a/components/auto-complete/public-api.ts b/components/auto-complete/public-api.ts index dcf7b1f5e65..fca0f6ee830 100644 --- a/components/auto-complete/public-api.ts +++ b/components/auto-complete/public-api.ts @@ -6,8 +6,8 @@ * found in the LICENSE file at https://github.com/NG-ZORRO/ng-zorro-antd/blob/master/LICENSE */ -export * from './nz-autocomplete.module'; -export * from './nz-autocomplete.component'; -export * from './nz-autocomplete-trigger.directive'; -export * from './nz-autocomplete-option.component'; -export * from './nz-autocomplete-optgroup.component'; +export * from './autocomplete.module'; +export * from './autocomplete.component'; +export * from './autocomplete-trigger.directive'; +export * from './autocomplete-option.component'; +export * from './autocomplete-optgroup.component';