-
+ nz-tooltip
+ [nzTitle]="nzTooltips[ i ]">
+
diff --git a/components/rate/nz-rate.component.ts b/components/rate/nz-rate.component.ts
index 33c7deb59c2..3e1bf26e6e2 100644
--- a/components/rate/nz-rate.component.ts
+++ b/components/rate/nz-rate.component.ts
@@ -2,21 +2,29 @@ import { LEFT_ARROW, RIGHT_ARROW } from '@angular/cdk/keycodes';
import {
forwardRef,
AfterViewInit,
+ ChangeDetectionStrategy,
+ ChangeDetectorRef,
Component,
ElementRef,
EventEmitter,
Input,
+ OnChanges,
OnInit,
Output,
Renderer2,
+ SimpleChanges,
TemplateRef,
- ViewChild
+ ViewChild,
+ ViewEncapsulation
} from '@angular/core';
import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms';
-import { toBoolean } from '../core/util/convert';
+import { ClassMap } from '../core/interface/interface';
+import { InputBoolean } from '../core/util/convert';
@Component({
+ changeDetection : ChangeDetectionStrategy.OnPush,
+ encapsulation : ViewEncapsulation.None,
selector : 'nz-rate',
preserveWhitespaces: false,
templateUrl : './nz-rate.component.html',
@@ -28,41 +36,31 @@ import { toBoolean } from '../core/util/convert';
}
]
})
-export class NzRateComponent implements OnInit, ControlValueAccessor, AfterViewInit {
- private _allowClear = true;
- private _allowHalf = false;
- private _disabled = false;
- private _count = 5;
- private _value = 0;
- private _autoFocus = false;
+export class NzRateComponent implements OnInit, ControlValueAccessor, AfterViewInit, OnChanges {
+ @ViewChild('ulElement') private ulElement: ElementRef;
+
+ @Input() @InputBoolean() nzAllowClear: boolean = true;
+ @Input() @InputBoolean() nzAllowHalf: boolean = false;
+ @Input() @InputBoolean() nzDisabled: boolean = false;
+ @Input() @InputBoolean() nzAutoFocus: boolean = false;
@Input() nzCharacter: TemplateRef
;
+ @Input() nzTooltips: string[] = [];
@Output() readonly nzOnBlur = new EventEmitter();
@Output() readonly nzOnFocus = new EventEmitter();
- @Output() readonly nzOnKeyDown = new EventEmitter();
@Output() readonly nzOnHoverChange = new EventEmitter();
- @ViewChild('ulElement') private ulElement: ElementRef;
- prefixCls = 'ant-rate';
- isInit = false;
+ @Output() readonly nzOnKeyDown = new EventEmitter();
+
+ classMap: ClassMap;
hasHalf = false;
- innerPrefixCls = `${this.prefixCls}-star`;
- classMap;
- starArray: number[] = [];
hoverValue = 0;
+ prefixCls = 'ant-rate';
+ innerPrefixCls = `${this.prefixCls}-star`;
isFocused = false;
- floatReg: RegExp = /^\d+(\.\d+)?$/;
-
- onChange: (value: number) => void = () => null;
- onTouched: () => void = () => null;
-
- @Input()
- set nzAutoFocus(value: boolean) {
- this._autoFocus = toBoolean(value);
- this.updateAutoFocus();
- }
+ isInit = false;
+ starArray: number[] = [];
- get nzAutoFocus(): boolean {
- return this._autoFocus;
- }
+ private _count = 5;
+ private _value = 0;
@Input()
set nzCount(value: number) {
@@ -77,61 +75,24 @@ export class NzRateComponent implements OnInit, ControlValueAccessor, AfterViewI
return this._count;
}
- @Input()
- set nzAllowHalf(value: boolean) {
- this._allowHalf = toBoolean(value);
- }
-
- get nzAllowHalf(): boolean {
- return this._allowHalf;
- }
-
- @Input()
- set nzAllowClear(value: boolean) {
- this._allowClear = toBoolean(value);
- }
-
- get nzAllowClear(): boolean {
- return this._allowClear;
- }
-
- get nzValue(): number {
- return this._value;
- }
+ get nzValue(): number { return this._value; }
set nzValue(input: number) {
- let value = input;
- if (this._value === value) {
+ if (this._value === input) {
return;
}
- this._value = value;
- if (this.floatReg.test(value.toString())) {
- value += 0.5;
- this.hasHalf = true;
- }
- this.hoverValue = value;
- }
- @Input()
- set nzDisabled(value: boolean) {
- this._disabled = toBoolean(value);
- this.setClassMap();
+ this._value = input;
+ this.hasHalf = !Number.isInteger(input);
+ this.hoverValue = Math.ceil(input);
}
- get nzDisabled(): boolean {
- return this._disabled;
+ constructor(private renderer: Renderer2, private cdr: ChangeDetectorRef) {
}
- setClassMap(): void {
- this.classMap = {
- [ this.prefixCls ] : true,
- [ `${this.prefixCls}-disabled` ]: this.nzDisabled
- };
- }
-
- updateAutoFocus(): void {
- if (this.isInit && !this.nzDisabled) {
- if (this.nzAutoFocus) {
+ ngOnChanges(changes: SimpleChanges): void {
+ if (changes.nzAutoFocus && !changes.nzAutoFocus.isFirstChange()) {
+ if (this.nzAutoFocus && !this.nzDisabled) {
this.renderer.setAttribute(this.ulElement.nativeElement, 'autofocus', 'autofocus');
} else {
this.renderer.removeAttribute(this.ulElement.nativeElement, 'autofocus');
@@ -139,19 +100,22 @@ export class NzRateComponent implements OnInit, ControlValueAccessor, AfterViewI
}
}
- clickRate(e: MouseEvent, index: number, isFull: boolean): void {
- e.stopPropagation();
+ ngOnInit(): void {
+ this.updateStarArray();
+ }
+
+ ngAfterViewInit(): void {
+ this.isInit = true;
+ }
+
+ onItemClick(index: number, isHalf: boolean): void {
if (this.nzDisabled) {
return;
}
- this.hasHalf = !isFull && this.nzAllowHalf;
- let actualValue = index + 1;
- this.hoverValue = actualValue;
+ this.hoverValue = index + 1;
- if (this.hasHalf) {
- actualValue -= 0.5;
- }
+ const actualValue = isHalf ? index + 0.5 : index + 1;
if (this.nzValue === actualValue) {
if (this.nzAllowClear) {
@@ -164,29 +128,20 @@ export class NzRateComponent implements OnInit, ControlValueAccessor, AfterViewI
}
}
- hoverRate(e: MouseEvent, index: number, isFull: boolean): void {
- e.stopPropagation();
- if (this.nzDisabled) {
- return;
- }
- const isHalf: boolean = !isFull && this.nzAllowHalf;
- if (this.hoverValue === index + 1 && isHalf === this.hasHalf) {
+ onItemHover(index: number, isHalf: boolean): void {
+ if (this.nzDisabled ||
+ (this.hoverValue === index + 1 && isHalf === this.hasHalf)) {
return;
}
this.hoverValue = index + 1;
- this.nzOnHoverChange.emit(this.hoverValue);
this.hasHalf = isHalf;
+ this.nzOnHoverChange.emit(this.hoverValue);
}
- leaveRate(e: MouseEvent): void {
- e.stopPropagation();
- let oldVal = this.nzValue;
- if (this.floatReg.test(oldVal.toString())) {
- oldVal += 0.5;
- this.hasHalf = true;
- }
- this.hoverValue = oldVal;
+ onRateLeave(): void {
+ this.hasHalf = !Number.isInteger(this.nzValue);
+ this.hoverValue = Math.ceil(this.nzValue);
}
onFocus(e: FocusEvent): void {
@@ -208,29 +163,23 @@ export class NzRateComponent implements OnInit, ControlValueAccessor, AfterViewI
}
onKeyDown(e: KeyboardEvent): void {
- const code = e.code;
- if ((code === 'ArrowRight' || e.keyCode === RIGHT_ARROW) && (this.nzValue < this.nzCount)) {
- if (this.nzAllowHalf) {
- this.nzValue += 0.5;
- } else {
- this.nzValue += 1;
- }
- this.onChange(this.nzValue);
- } else if ((code === 'ArrowLeft' || e.keyCode === LEFT_ARROW) && (this.nzValue > 0)) {
- if (this.nzAllowHalf) {
- this.nzValue -= 0.5;
- } else {
- this.nzValue -= 1;
- }
+ const oldVal = this.nzValue;
+
+ if (e.keyCode === RIGHT_ARROW && (this.nzValue < this.nzCount)) {
+ this.nzValue += this.nzAllowHalf ? 0.5 : 1;
+ } else if (e.keyCode === LEFT_ARROW && (this.nzValue > 0)) {
+ this.nzValue -= this.nzAllowHalf ? 0.5 : 1;
+ }
+
+ if (oldVal !== this.nzValue) {
this.onChange(this.nzValue);
+ this.nzOnKeyDown.emit(e);
+ this.cdr.markForCheck();
}
- this.nzOnKeyDown.emit(e);
- e.preventDefault();
}
setClasses(i: number): object {
return {
- [ this.innerPrefixCls ] : true,
[ `${this.innerPrefixCls}-full` ] : (i + 1 < this.hoverValue) || (!this.hasHalf) && (i + 1 === this.hoverValue),
[ `${this.innerPrefixCls}-half` ] : (this.hasHalf) && (i + 1 === this.hoverValue),
[ `${this.innerPrefixCls}-active` ] : (this.hasHalf) && (i + 1 === this.hoverValue),
@@ -239,16 +188,19 @@ export class NzRateComponent implements OnInit, ControlValueAccessor, AfterViewI
};
}
- updateStarArray(): void {
- let index = 0;
- this.starArray = [];
- while (index < this.nzCount) {
- this.starArray.push(index++);
- }
+ private updateStarArray(): void {
+ this.starArray = Array(this.nzCount).fill(0).map((_, i) => i);
}
+ // #region Implement `ControlValueAccessor`
+
writeValue(value: number | null): void {
this.nzValue = value || 0;
+ this.cdr.markForCheck();
+ }
+
+ setDisabledState(isDisabled: boolean): void {
+ this.nzDisabled = isDisabled;
}
registerOnChange(fn: (_: number) => void): void {
@@ -259,19 +211,8 @@ export class NzRateComponent implements OnInit, ControlValueAccessor, AfterViewI
this.onTouched = fn;
}
- setDisabledState(isDisabled: boolean): void {
- this.nzDisabled = isDisabled;
- }
-
- constructor(private renderer: Renderer2) {
- }
-
- ngOnInit(): void {
- this.setClassMap();
- this.updateStarArray();
- }
+ onChange: (value: number) => void = () => null;
+ onTouched: () => void = () => null;
- ngAfterViewInit(): void {
- this.isInit = true;
- }
+ // #endregion
}
diff --git a/components/rate/nz-rate.module.ts b/components/rate/nz-rate.module.ts
index 98578846426..3672d82bff0 100644
--- a/components/rate/nz-rate.module.ts
+++ b/components/rate/nz-rate.module.ts
@@ -2,12 +2,15 @@ import { CommonModule } from '@angular/common';
import { NgModule } from '@angular/core';
import { NzIconModule } from '../icon/nz-icon.module';
+import { NzToolTipModule } from '../tooltip/nz-tooltip.module';
+
+import { NzRateItemComponent } from './nz-rate-item.component';
import { NzRateComponent } from './nz-rate.component';
@NgModule({
exports : [ NzRateComponent ],
- declarations: [ NzRateComponent ],
- imports : [ CommonModule, NzIconModule ]
+ declarations: [ NzRateComponent, NzRateItemComponent ],
+ imports : [ CommonModule, NzIconModule, NzToolTipModule ]
})
export class NzRateModule {
}
diff --git a/components/rate/nz-rate.spec.ts b/components/rate/nz-rate.spec.ts
index a3cdb23d95c..79349815225 100644
--- a/components/rate/nz-rate.spec.ts
+++ b/components/rate/nz-rate.spec.ts
@@ -1,10 +1,11 @@
-import { Component, ViewChild } from '@angular/core';
+import { Component, DebugElement, ViewChild } from '@angular/core';
import { fakeAsync, flush, TestBed } from '@angular/core/testing';
import { FormsModule, FormBuilder, FormGroup, ReactiveFormsModule } from '@angular/forms';
import { By } from '@angular/platform-browser';
-import { dispatchEvent, dispatchFakeEvent } from '../core/testing';
+import { dispatchEvent, dispatchFakeEvent, dispatchKeyboardEvent } from '../core/testing';
+import { LEFT_ARROW, RIGHT_ARROW } from '@angular/cdk/keycodes';
import { NzRateComponent } from './nz-rate.component';
import { NzRateModule } from './nz-rate.module';
@@ -44,7 +45,7 @@ describe('rate', () => {
it('should click work', fakeAsync(() => {
fixture.detectChanges();
expect(testComponent.value).toBe(0);
- rate.nativeElement.firstElementChild.children[ 3 ].firstElementChild.click();
+ rate.nativeElement.firstElementChild.children[ 3 ].firstElementChild.firstElementChild.click();
fixture.detectChanges();
flush();
fixture.detectChanges();
@@ -55,7 +56,7 @@ describe('rate', () => {
testComponent.allowHalf = true;
fixture.detectChanges();
expect(testComponent.value).toBe(0);
- rate.nativeElement.firstElementChild.children[ 3 ].firstElementChild.click();
+ rate.nativeElement.firstElementChild.children[ 3 ].firstElementChild.children[ 1 ].click();
fixture.detectChanges();
flush();
fixture.detectChanges();
@@ -66,13 +67,13 @@ describe('rate', () => {
testComponent.allowClear = false;
fixture.detectChanges();
expect(testComponent.value).toBe(0);
- rate.nativeElement.firstElementChild.children[ 3 ].firstElementChild.click();
+ rate.nativeElement.firstElementChild.children[ 3 ].firstElementChild.firstElementChild.click();
fixture.detectChanges();
flush();
fixture.detectChanges();
expect(testComponent.value).toBe(4);
expect(testComponent.modelChange).toHaveBeenCalledTimes(1);
- rate.nativeElement.firstElementChild.children[ 3 ].firstElementChild.click();
+ rate.nativeElement.firstElementChild.children[ 3 ].firstElementChild.firstElementChild.click();
fixture.detectChanges();
flush();
fixture.detectChanges();
@@ -80,7 +81,7 @@ describe('rate', () => {
expect(testComponent.modelChange).toHaveBeenCalledTimes(1);
testComponent.allowClear = true;
fixture.detectChanges();
- rate.nativeElement.firstElementChild.children[ 3 ].firstElementChild.click();
+ rate.nativeElement.firstElementChild.children[ 3 ].firstElementChild.firstElementChild.click();
fixture.detectChanges();
flush();
fixture.detectChanges();
@@ -91,7 +92,7 @@ describe('rate', () => {
testComponent.disabled = true;
fixture.detectChanges();
expect(testComponent.value).toBe(0);
- rate.nativeElement.firstElementChild.children[ 3 ].firstElementChild.click();
+ rate.nativeElement.firstElementChild.children[ 3 ].firstElementChild.firstElementChild.click();
fixture.detectChanges();
flush();
fixture.detectChanges();
@@ -102,7 +103,7 @@ describe('rate', () => {
fixture.detectChanges();
expect(rate.nativeElement.firstElementChild.children.length).toBe(5);
expect(testComponent.value).toBe(0);
- rate.nativeElement.firstElementChild.children[ 3 ].firstElementChild.click();
+ rate.nativeElement.firstElementChild.children[ 3 ].firstElementChild.firstElementChild.click();
fixture.detectChanges();
flush();
fixture.detectChanges();
@@ -137,7 +138,7 @@ describe('rate', () => {
});
it('should hover rate work', () => {
fixture.detectChanges();
- dispatchFakeEvent(rate.nativeElement.firstElementChild.children[ 3 ].firstElementChild, 'mouseover');
+ dispatchFakeEvent(rate.nativeElement.firstElementChild.children[ 3 ].firstElementChild.firstElementChild, 'mouseover');
fixture.detectChanges();
expect(rate.nativeElement.firstElementChild.children[ 3 ].classList).toContain('ant-rate-star-full');
expect(testComponent.onHoverChange).toHaveBeenCalledWith(4);
@@ -154,46 +155,37 @@ describe('rate', () => {
expect(testComponent.onHoverChange).toHaveBeenCalledTimes(1);
});
it('should keydown work', () => {
- const leftArrowEvent = new KeyboardEvent('keydown', {
- code: 'ArrowLeft'
- });
- const rightArrowEvent = new KeyboardEvent('keydown', {
- code: 'ArrowRight'
- });
fixture.detectChanges();
expect(testComponent.value).toBe(0);
- dispatchEvent(rate.nativeElement.firstElementChild, leftArrowEvent);
+ dispatchKeyboardEvent(rate.nativeElement.firstElementChild, 'keydown', LEFT_ARROW);
fixture.detectChanges();
expect(testComponent.value).toBe(0);
expect(testComponent.modelChange).toHaveBeenCalledTimes(0);
- dispatchEvent(rate.nativeElement.firstElementChild, rightArrowEvent);
+ dispatchKeyboardEvent(rate.nativeElement.firstElementChild, 'keydown', RIGHT_ARROW);
fixture.detectChanges();
expect(testComponent.value).toBe(1);
expect(testComponent.modelChange).toHaveBeenCalledTimes(1);
- dispatchEvent(rate.nativeElement.firstElementChild, leftArrowEvent);
+ dispatchKeyboardEvent(rate.nativeElement.firstElementChild, 'keydown', LEFT_ARROW);
fixture.detectChanges();
expect(testComponent.value).toBe(0);
expect(testComponent.modelChange).toHaveBeenCalledTimes(2);
testComponent.allowHalf = true;
fixture.detectChanges();
- dispatchEvent(rate.nativeElement.firstElementChild, rightArrowEvent);
+ dispatchKeyboardEvent(rate.nativeElement.firstElementChild, 'keydown', RIGHT_ARROW);
fixture.detectChanges();
expect(testComponent.value).toBe(0.5);
expect(testComponent.modelChange).toHaveBeenCalledTimes(3);
- dispatchEvent(rate.nativeElement.firstElementChild, leftArrowEvent);
+ dispatchKeyboardEvent(rate.nativeElement.firstElementChild, 'keydown', LEFT_ARROW);
fixture.detectChanges();
expect(testComponent.value).toBe(0);
expect(testComponent.modelChange).toHaveBeenCalledTimes(4);
});
- it('should right keydown work', fakeAsync(() => {
- const rightArrowEvent = new KeyboardEvent('keydown', {
- code: 'ArrowRight'
- });
+ it('should right keydown not dispatch change reached limit', fakeAsync(() => {
testComponent.value = 5;
fixture.detectChanges();
flush();
fixture.detectChanges();
- dispatchEvent(rate.nativeElement.firstElementChild, rightArrowEvent);
+ dispatchKeyboardEvent(rate.nativeElement.firstElementChild, 'keydown', RIGHT_ARROW);
fixture.detectChanges();
expect(testComponent.value).toBe(5);
expect(testComponent.modelChange).toHaveBeenCalledTimes(0);
@@ -220,7 +212,7 @@ describe('rate', () => {
it('should set disabled work', fakeAsync(() => {
flush();
expect(testComponent.formGroup.get('rate').value).toBe(1);
- rate.nativeElement.firstElementChild.children[3].firstElementChild.click();
+ rate.nativeElement.firstElementChild.children[3].firstElementChild.firstElementChild.click();
fixture.detectChanges();
expect(testComponent.formGroup.get('rate').value).toBe(4);
fixture.detectChanges();
@@ -231,7 +223,7 @@ describe('rate', () => {
fixture.detectChanges();
flush();
fixture.detectChanges();
- rate.nativeElement.firstElementChild.children[3].firstElementChild.click();
+ rate.nativeElement.firstElementChild.children[3].firstElementChild.firstElementChild.click();
fixture.detectChanges();
expect(testComponent.formGroup.get('rate').value).toBe(2);
}));