diff --git a/components/mention/demo/async.ts b/components/mention/demo/async.ts
index 7362c97225..06c1757d1b 100755
--- a/components/mention/demo/async.ts
+++ b/components/mention/demo/async.ts
@@ -7,7 +7,7 @@ import { MentionOnSearchTypes } from 'ng-zorro-antd/mention';
encapsulation: ViewEncapsulation.None,
template: `
-
+
`
})
diff --git a/components/mention/demo/avatar.md b/components/mention/demo/avatar.md
index ec5e19efc7..576260ae02 100755
--- a/components/mention/demo/avatar.md
+++ b/components/mention/demo/avatar.md
@@ -1,5 +1,5 @@
---
-order: 3
+order: 6
title:
zh-CN: 头像
en-US: Icon Image
diff --git a/components/mention/demo/avatar.ts b/components/mention/demo/avatar.ts
index 5629b6abed..fa7dc0e07c 100755
--- a/components/mention/demo/avatar.ts
+++ b/components/mention/demo/avatar.ts
@@ -5,7 +5,7 @@ import { Component, ViewEncapsulation } from '@angular/core';
encapsulation: ViewEncapsulation.None,
template: `
-
+
{{ framework.name }} - {{ framework.type }}
diff --git a/components/mention/demo/basic.ts b/components/mention/demo/basic.ts
index f3d3d83979..1974ac8b52 100644
--- a/components/mention/demo/basic.ts
+++ b/components/mention/demo/basic.ts
@@ -5,13 +5,14 @@ import { Component, ViewEncapsulation } from '@angular/core';
encapsulation: ViewEncapsulation.None,
template: `
-
+ >
`
})
diff --git a/components/mention/demo/custom-tag.md b/components/mention/demo/custom-tag.md
index 17d3f5c906..358a912fa7 100755
--- a/components/mention/demo/custom-tag.md
+++ b/components/mention/demo/custom-tag.md
@@ -1,5 +1,5 @@
---
-order: 2
+order: 7
title:
zh-CN: 自定义建议
en-US: Customize Suggestion
diff --git a/components/mention/demo/custom-tag.ts b/components/mention/demo/custom-tag.ts
index 982c26ccc5..efd8dd91d4 100644
--- a/components/mention/demo/custom-tag.ts
+++ b/components/mention/demo/custom-tag.ts
@@ -5,7 +5,7 @@ import { Component, ViewEncapsulation } from '@angular/core';
encapsulation: ViewEncapsulation.None,
template: `
-
+
{{ framework.name }} - {{ framework.type }}
diff --git a/components/mention/demo/controlled.md b/components/mention/demo/form.md
similarity index 95%
rename from components/mention/demo/controlled.md
rename to components/mention/demo/form.md
index aef355b3ed..d074b04916 100755
--- a/components/mention/demo/controlled.md
+++ b/components/mention/demo/form.md
@@ -1,5 +1,5 @@
---
-order: 4
+order: 2
title:
zh-CN: 配合 Form 使用
en-US: With Form
diff --git a/components/mention/demo/controlled.ts b/components/mention/demo/form.ts
similarity index 87%
rename from components/mention/demo/controlled.ts
rename to components/mention/demo/form.ts
index c74bc8da80..435d3f3950 100644
--- a/components/mention/demo/controlled.ts
+++ b/components/mention/demo/form.ts
@@ -4,7 +4,7 @@ import { AbstractControl, FormBuilder, FormControl, FormGroup, Validators } from
import { NzMentionComponent } from 'ng-zorro-antd/mention';
@Component({
- selector: 'nz-demo-mention-controlled',
+ selector: 'nz-demo-mention-form',
encapsulation: ViewEncapsulation.None,
template: `
`
})
-export class NzDemoMentionControlledComponent implements OnInit {
+export class NzDemoMentionFormComponent implements OnInit {
suggestions = ['afc163', 'benjycui', 'yiminghe', 'RaoHai', '中文', 'にほんご'];
validateForm!: FormGroup;
@ViewChild('mentions', { static: true }) mentionChild!: NzMentionComponent;
diff --git a/components/mention/demo/multilines.md b/components/mention/demo/multilines.md
index 4692c9b92d..fbfe4545ba 100755
--- a/components/mention/demo/multilines.md
+++ b/components/mention/demo/multilines.md
@@ -1,5 +1,5 @@
---
-order: 5
+order: 8
title:
zh-CN: 多行
en-US: Multi-lines Mode
diff --git a/components/mention/demo/placement.md b/components/mention/demo/placement.md
index ef14ea14a7..4dcc1e58f4 100755
--- a/components/mention/demo/placement.md
+++ b/components/mention/demo/placement.md
@@ -1,5 +1,5 @@
---
-order: 0
+order: 5
title:
zh-CN: 向上展开
en-US: Placement
diff --git a/components/mention/demo/placement.ts b/components/mention/demo/placement.ts
index da6c35b25f..5ec8b3d7eb 100755
--- a/components/mention/demo/placement.ts
+++ b/components/mention/demo/placement.ts
@@ -5,7 +5,13 @@ import { Component, ViewEncapsulation } from '@angular/core';
encapsulation: ViewEncapsulation.None,
template: `
-
+
`
})
diff --git a/components/mention/demo/multiple-trigger.md b/components/mention/demo/prefix.md
similarity index 96%
rename from components/mention/demo/multiple-trigger.md
rename to components/mention/demo/prefix.md
index e1467470fb..cc27d3d295 100755
--- a/components/mention/demo/multiple-trigger.md
+++ b/components/mention/demo/prefix.md
@@ -1,5 +1,5 @@
---
-order: 8
+order: 3
title:
zh-CN: 自定义触发字符
en-US: Customize Trigger Token
diff --git a/components/mention/demo/multiple-trigger.ts b/components/mention/demo/prefix.ts
similarity index 86%
rename from components/mention/demo/multiple-trigger.ts
rename to components/mention/demo/prefix.ts
index 818e1ca0fc..3040d39ce7 100755
--- a/components/mention/demo/multiple-trigger.ts
+++ b/components/mention/demo/prefix.ts
@@ -3,20 +3,21 @@ import { Component, ViewEncapsulation } from '@angular/core';
import { MentionOnSearchTypes } from 'ng-zorro-antd/mention';
@Component({
- selector: 'nz-demo-mention-multiple-trigger',
+ selector: 'nz-demo-mention-prefix',
encapsulation: ViewEncapsulation.None,
template: `
-
+ >
`
})
-export class NzDemoMentionMultipleTriggerComponent {
+export class NzDemoMentionPrefixComponent {
inputValue?: string;
suggestions: string[] = [];
users = ['afc163', 'benjycui', 'yiminghe', 'RaoHai', '中文', 'にほんご'];
diff --git a/components/mention/demo/readonly.md b/components/mention/demo/readonly.md
index e867f2d627..b45a2e2708 100755
--- a/components/mention/demo/readonly.md
+++ b/components/mention/demo/readonly.md
@@ -1,5 +1,5 @@
---
-order: 7
+order: 4
title:
zh-CN: 无效或只读
en-US: disabled or readOnly
diff --git a/components/mention/demo/readonly.ts b/components/mention/demo/readonly.ts
index 6561331f56..7a0d36e8f9 100755
--- a/components/mention/demo/readonly.ts
+++ b/components/mention/demo/readonly.ts
@@ -4,16 +4,25 @@ import { Component, ViewEncapsulation } from '@angular/core';
selector: 'nz-demo-mention-readonly',
encapsulation: ViewEncapsulation.None,
template: `
-
-
+
-
+ >
+
+
+
`
})
diff --git a/components/mention/demo/status.md b/components/mention/demo/status.md
new file mode 100644
index 0000000000..e1f17dc78f
--- /dev/null
+++ b/components/mention/demo/status.md
@@ -0,0 +1,14 @@
+---
+order: 10
+title:
+ zh-CN: 自定义状态
+ en-US: Status
+---
+
+## zh-CN
+
+使用 `nzStatus` 为 Mentions 添加状态。可选 `error` 或者 `warning`。
+
+## en-US
+
+Add status to Mentions with `nzStatus`, which could be `error` or `warning`。
diff --git a/components/mention/demo/status.ts b/components/mention/demo/status.ts
new file mode 100644
index 0000000000..3175ced4d0
--- /dev/null
+++ b/components/mention/demo/status.ts
@@ -0,0 +1,18 @@
+import { Component, ViewEncapsulation } from '@angular/core';
+
+@Component({
+ selector: 'nz-demo-mention-status',
+ encapsulation: ViewEncapsulation.None,
+ template: `
+
+
+
+
+
+
+ `
+})
+export class NzDemoMentionStatusComponent {
+ inputValue: string = '@afc163';
+ suggestions = ['afc163', 'benjycui', 'yiminghe', 'RaoHai', '中文', 'にほんご'];
+}
diff --git a/components/mention/doc/index.en-US.md b/components/mention/doc/index.en-US.md
index bddbfe4647..03b713eaa6 100644
--- a/components/mention/doc/index.en-US.md
+++ b/components/mention/doc/index.en-US.md
@@ -38,6 +38,7 @@ import { NzMentionModule } from 'ng-zorro-antd/mention';
| `[nzPlacement]` | The position of the suggestion relative to the target, which can be one of top and bottom | `'button' \| 'top'` | `'bottom'` |
| `[nzPrefix]` | Character which will trigger Mention to show mention list | `string \| string[]` | `'@'` |
| `[nzSuggestions]` | Suggestion content | `any[]` | `[]` |
+| `[nzStatus]` | Set validation status | `'error' \| 'warning'` | - |
| `[nzValueWith]` | Function that maps an suggestion's value | `(any) => string \| (value: string) => string` |
| `(nzOnSelect)` | Callback function called when select from suggestions | `EventEmitter` | - |
| `(nzOnSearchChange)` | Callback function called when search content changes| `EventEmitter` | - |
diff --git a/components/mention/doc/index.zh-CN.md b/components/mention/doc/index.zh-CN.md
index 5f7cd3c779..b1b666ee98 100644
--- a/components/mention/doc/index.zh-CN.md
+++ b/components/mention/doc/index.zh-CN.md
@@ -39,6 +39,7 @@ import { NzMentionModule } from 'ng-zorro-antd/mention';
| `[nzPlacement]` | 建议框位置 | `'bottom' \| 'top'` | `'bottom'` |
| `[nzPrefix]` | 触发弹出下拉框的字符 | `string \| string[]` | `'@'` |
| `[nzSuggestions]` | 建议内容 | `any[]` | `[]` |
+| `[nzStatus]` | 设置校验状态 | `'error' \| 'warning'` | - |
| `[nzValueWith]` | 建议选项的取值方法 | `(any) => string \| (value: string) => string` |
| `(nzOnSelect)` | 下拉框选择建议时回调 | `EventEmitter` | - |
| `(nzOnSearchChange)` | 输入框中 @ 变化时回调 | `EventEmitter` | - |
diff --git a/components/mention/mention.component.ts b/components/mention/mention.component.ts
index 489584c59e..d38d13ce78 100644
--- a/components/mention/mention.component.ts
+++ b/components/mention/mention.component.ts
@@ -32,6 +32,7 @@ import {
Optional,
Output,
QueryList,
+ Renderer2,
SimpleChanges,
TemplateRef,
ViewChild,
@@ -43,8 +44,8 @@ import { startWith, switchMap, takeUntil } from 'rxjs/operators';
import { DEFAULT_MENTION_BOTTOM_POSITIONS, DEFAULT_MENTION_TOP_POSITIONS } from 'ng-zorro-antd/core/overlay';
import { NzDestroyService } from 'ng-zorro-antd/core/services';
-import { BooleanInput, NzSafeAny } from 'ng-zorro-antd/core/types';
-import { getCaretCoordinates, getMentions, InputBoolean } from 'ng-zorro-antd/core/util';
+import { BooleanInput, NgClassInterface, NzSafeAny, NzStatus } from 'ng-zorro-antd/core/types';
+import { getCaretCoordinates, getMentions, getStatusClassNames, InputBoolean } from 'ng-zorro-antd/core/util';
import { NZ_MENTION_CONFIG } from './config';
import { NzMentionSuggestionDirective } from './mention-suggestions';
@@ -117,6 +118,7 @@ export class NzMentionComponent implements OnDestroy, OnInit, AfterViewInit, OnC
@Input() nzNotFoundContent: string = '无匹配结果,轻敲空格完成输入';
@Input() nzPlacement: MentionPlacement = 'bottom';
@Input() nzSuggestions: NzSafeAny[] = [];
+ @Input() nzStatus?: NzStatus;
@Output() readonly nzOnSelect: EventEmitter = new EventEmitter();
@Output() readonly nzOnSearchChange: EventEmitter = new EventEmitter();
@@ -137,6 +139,10 @@ export class NzMentionComponent implements OnDestroy, OnInit, AfterViewInit, OnC
suggestionTemplate: TemplateRef<{ $implicit: NzSafeAny }> | null = null;
activeIndex = -1;
dir: Direction = 'ltr';
+ // status
+ prefixCls: string = 'ant-mentions';
+ statusCls: NgClassInterface = {};
+ nzHasFeedback: boolean = false;
private previousValue: string | null = null;
private cursorMention: string | null = null;
@@ -166,6 +172,8 @@ export class NzMentionComponent implements OnDestroy, OnInit, AfterViewInit, OnC
private cdr: ChangeDetectorRef,
private overlay: Overlay,
private viewContainerRef: ViewContainerRef,
+ private elementRef: ElementRef,
+ private renderer: Renderer2,
private nzMentionService: NzMentionService,
private destroy$: NzDestroyService
) {}
@@ -185,13 +193,17 @@ export class NzMentionComponent implements OnDestroy, OnInit, AfterViewInit, OnC
}
ngOnChanges(changes: SimpleChanges): void {
- if (changes.hasOwnProperty('nzSuggestions')) {
+ const { nzSuggestions, nzStatus } = changes;
+ if (nzSuggestions) {
if (this.isOpen) {
this.previousValue = null;
this.activeIndex = -1;
this.resetDropdown(false);
}
}
+ if (nzStatus) {
+ this.setStatusStyles();
+ }
}
ngAfterViewInit(): void {
@@ -464,4 +476,16 @@ export class NzMentionComponent implements OnDestroy, OnInit, AfterViewInit, OnC
.withPush(false);
return this.positionStrategy;
}
+
+ private setStatusStyles(): void {
+ // render status if nzStatus is set
+ this.statusCls = getStatusClassNames(this.prefixCls, this.nzStatus, this.nzHasFeedback);
+ Object.keys(this.statusCls).forEach(status => {
+ if (this.statusCls[status]) {
+ this.renderer.addClass(this.elementRef.nativeElement, status);
+ } else {
+ this.renderer.removeClass(this.elementRef.nativeElement, status);
+ }
+ });
+ }
}
diff --git a/components/mention/nz-mention.spec.ts b/components/mention/nz-mention.spec.ts
index 5ecbc80f7c..64f17f3bc7 100644
--- a/components/mention/nz-mention.spec.ts
+++ b/components/mention/nz-mention.spec.ts
@@ -2,7 +2,7 @@ import { BidiModule, Direction, Directionality } from '@angular/cdk/bidi';
import { DOWN_ARROW, ENTER, ESCAPE, RIGHT_ARROW, TAB, UP_ARROW } from '@angular/cdk/keycodes';
import { OverlayContainer } from '@angular/cdk/overlay';
import { ScrollDispatcher } from '@angular/cdk/scrolling';
-import { ApplicationRef, Component, NgZone, ViewChild } from '@angular/core';
+import { ApplicationRef, Component, DebugElement, NgZone, ViewChild } from '@angular/core';
import { ComponentFixture, fakeAsync, flush, inject, TestBed, tick, waitForAsync } from '@angular/core/testing';
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import { By } from '@angular/platform-browser';
@@ -16,6 +16,7 @@ import {
MockNgZone,
typeInElement
} from 'ng-zorro-antd/core/testing';
+import { NzStatus } from 'ng-zorro-antd/core/types';
import { NzIconTestModule } from 'ng-zorro-antd/icon/testing';
import { NzInputModule } from '../input';
@@ -42,7 +43,12 @@ describe('mention', () => {
ReactiveFormsModule,
NzIconTestModule
],
- declarations: [NzTestSimpleMentionComponent, NzTestPropertyMentionComponent, NzTestDirMentionComponent],
+ declarations: [
+ NzTestSimpleMentionComponent,
+ NzTestPropertyMentionComponent,
+ NzTestDirMentionComponent,
+ NzTestStatusMentionComponent
+ ],
providers: [
{ provide: Directionality, useFactory: () => ({ value: dir }) },
{ provide: ScrollDispatcher, useFactory: () => ({ scrolled: () => scrolledSubject }) },
@@ -199,15 +205,13 @@ describe('mention', () => {
it('should support switch trigger', fakeAsync(() => {
fixture.componentInstance.inputTrigger = true;
fixture.detectChanges();
- const input = fixture.debugElement.query(By.css('input')).nativeElement;
+ const textareaWithSingleLine = fixture.debugElement.query(By.css('textarea')).nativeElement;
const mention = fixture.componentInstance.mention;
+ expect(textareaWithSingleLine).toBeTruthy();
- expect(fixture.debugElement.query(By.css('textarea'))).toBeFalsy();
- expect(input).toBeTruthy();
-
- input.value = '@a';
+ textareaWithSingleLine.value = '@a';
fixture.detectChanges();
- dispatchFakeEvent(input, 'click');
+ dispatchFakeEvent(textareaWithSingleLine, 'click');
fixture.detectChanges();
flush();
@@ -521,6 +525,30 @@ describe('mention', () => {
expect(fixture.componentInstance.mention.getMentions().join(',')).toBe('@Angular,@ant-design,@你好,@@ng,#ng');
});
});
+
+ describe('status', () => {
+ let fixture: ComponentFixture;
+ let mention: DebugElement;
+
+ beforeEach(() => {
+ fixture = TestBed.createComponent(NzTestStatusMentionComponent);
+ mention = fixture.debugElement.query(By.directive(NzMentionComponent));
+ fixture.detectChanges();
+ });
+
+ it('should className with status correct', () => {
+ fixture.detectChanges();
+ expect(mention.nativeElement.classList).toContain('ant-mentions-status-error');
+
+ fixture.componentInstance.status = 'warning';
+ fixture.detectChanges();
+ expect(mention.nativeElement.classList).toContain('ant-mentions-status-warning');
+
+ fixture.componentInstance.status = '';
+ fixture.detectChanges();
+ expect(mention.nativeElement.classList).not.toContain('ant-mentions-status-warning');
+ });
+ });
});
@Component({
@@ -533,7 +561,7 @@ describe('mention', () => {
[(ngModel)]="inputValue"
nzMentionTrigger
>
-
+
`
})
@@ -605,7 +633,7 @@ class NzTestPropertyMentionComponent {
template: `
-
+
`
@@ -613,3 +641,14 @@ class NzTestPropertyMentionComponent {
class NzTestDirMentionComponent {
direction: Direction = 'ltr';
}
+
+@Component({
+ template: `
+
+
+
+ `
+})
+class NzTestStatusMentionComponent {
+ status: NzStatus = 'error';
+}
diff --git a/components/mention/style/patch.less b/components/mention/style/patch.less
index aebb4eb1c6..fe3269fd18 100644
--- a/components/mention/style/patch.less
+++ b/components/mention/style/patch.less
@@ -1,21 +1,30 @@
-.ant-mentions {
- border-width: 0;
- display: unset; // to make sure input:hover work
+.@{mention-prefix-cls} {
+ &-dropdown {
+ top: 100%;
+ left: 12px;
+ position: relative;
+ width: 100%;
+ margin-top: 8px;
+ margin-bottom: 4px;
+ }
- &:hover {
- border-right-width: 0;
+ &:focus-within {
+ .active();
}
- & > textarea {
- border: @border-width-base @border-style-base @border-color-base;
+ &&-status-error {
+ &:not(.@{mention-prefix-cls}-disabled):not(.@{mention-prefix-cls}-borderless).@{mention-prefix-cls} {
+ &:focus-within {
+ .active(@error-color, @error-color-hover, @error-color-outline);
+ }
+ }
}
-}
-.ant-mentions-dropdown {
- top: 100%;
- left: 12px;
- position: relative;
- width: 100%;
- margin-top: 4px;
- margin-bottom: 4px;
+ &&-status-warning {
+ &:not(.@{mention-prefix-cls}-disabled):not(.@{mention-prefix-cls}-borderless).@{mention-prefix-cls} {
+ &:focus-within {
+ .active(@warning-color, @warning-color-hover, @warning-color-outline);
+ }
+ }
+ }
}