UNPKG

ng-zorro-antd

Version:

An enterprise-class UI components based on Ant Design and Angular

807 lines (798 loc) 24.9 kB
import { OverlayConfig, ConnectionPositionPair, Overlay, OverlayModule } from '@angular/cdk/overlay'; import { DOCUMENT, CommonModule } from '@angular/common'; import { Directive, Injectable, forwardRef, EventEmitter, ElementRef, Component, ChangeDetectionStrategy, Optional, Inject, ChangeDetectorRef, ViewContainerRef, Input, Output, ViewChild, TemplateRef, ContentChild, NgModule } from '@angular/core'; import { NG_VALUE_ACCESSOR, FormsModule } from '@angular/forms'; import { NzIconModule } from 'ng-zorro-antd/icon'; import { Subject, merge, fromEvent } from 'rxjs'; import { __decorate, __metadata } from 'tslib'; import { ENTER, LEFT_ARROW, RIGHT_ARROW, TAB, ESCAPE, UP_ARROW, DOWN_ARROW } from '@angular/cdk/keycodes'; import { TemplatePortal } from '@angular/cdk/portal'; import { getMentions, getCaretCoordinates, DEFAULT_MENTION_BOTTOM_POSITIONS, DEFAULT_MENTION_TOP_POSITIONS, InputBoolean } from 'ng-zorro-antd/core'; /** * @fileoverview added by tsickle * @suppress {checkTypes,constantProperty,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc */ class NzMentionSuggestionDirective { } NzMentionSuggestionDirective.decorators = [ { type: Directive, args: [{ selector: '[nzMentionSuggestion]', exportAs: 'nzMentionSuggestion' },] } ]; /** * @fileoverview added by tsickle * @suppress {checkTypes,constantProperty,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc */ class NzMentionService { constructor() { this.triggerChange$ = new Subject(); } /** * @return {?} */ triggerChanged() { return this.triggerChange$.asObservable(); } /** * @param {?} trigger * @return {?} */ registerTrigger(trigger) { if (this.trigger !== trigger) { this.trigger = trigger; this.triggerChange$.next(trigger); } } /** * @return {?} */ ngOnDestroy() { this.triggerChange$.complete(); } } NzMentionService.decorators = [ { type: Injectable } ]; if (false) { /** * @type {?} * @private */ NzMentionService.prototype.trigger; /** * @type {?} * @private */ NzMentionService.prototype.triggerChange$; } /** * @fileoverview added by tsickle * @suppress {checkTypes,constantProperty,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc */ /** @type {?} */ const NZ_MENTION_TRIGGER_ACCESSOR = { provide: NG_VALUE_ACCESSOR, useExisting: forwardRef((/** * @return {?} */ () => NzMentionTriggerDirective)), multi: true }; class NzMentionTriggerDirective { /** * @param {?} el * @param {?} nzMentionService */ constructor(el, nzMentionService) { this.el = el; this.nzMentionService = nzMentionService; this.onFocusin = new EventEmitter(); this.onBlur = new EventEmitter(); this.onInput = new EventEmitter(); this.onKeydown = new EventEmitter(); this.onClick = new EventEmitter(); } /** * @return {?} */ completeEvents() { this.onFocusin.complete(); this.onBlur.complete(); this.onInput.complete(); this.onKeydown.complete(); this.onClick.complete(); } /** * @param {?=} caretPos * @return {?} */ focus(caretPos) { this.el.nativeElement.focus(); this.el.nativeElement.setSelectionRange(caretPos, caretPos); } /** * @param {?} mention * @return {?} */ insertMention(mention) { /** @type {?} */ const value = this.el.nativeElement.value; /** @type {?} */ const insertValue = mention.mention.trim() + ' '; /** @type {?} */ const newValue = [ value.slice(0, mention.startPos + 1), insertValue, value.slice(mention.endPos, value.length) ].join(''); this.el.nativeElement.value = newValue; this.focus(mention.startPos + insertValue.length + 1); this.onChange(newValue); this.value = newValue; } /** * @param {?} value * @return {?} */ writeValue(value) { this.value = value; if (typeof value === 'string') { this.el.nativeElement.value = value; } else { this.el.nativeElement.value = ''; } } /** * @param {?} fn * @return {?} */ registerOnChange(fn) { this.onChange = fn; } /** * @param {?} fn * @return {?} */ registerOnTouched(fn) { this.onTouched = fn; } /** * @return {?} */ ngAfterViewInit() { this.nzMentionService.registerTrigger(this); } /** * @return {?} */ ngOnDestroy() { this.completeEvents(); } } NzMentionTriggerDirective.decorators = [ { type: Directive, args: [{ selector: 'input[nzMentionTrigger], textarea[nzMentionTrigger]', exportAs: 'nzMentionTrigger', providers: [NZ_MENTION_TRIGGER_ACCESSOR], host: { autocomplete: 'off', '(focusin)': 'onFocusin.emit()', '(blur)': 'onBlur.emit()', '(input)': 'onInput.emit($event)', '(keydown)': 'onKeydown.emit($event)', '(click)': 'onClick.emit($event)' } },] } ]; /** @nocollapse */ NzMentionTriggerDirective.ctorParameters = () => [ { type: ElementRef }, { type: NzMentionService } ]; if (false) { /** @type {?} */ NzMentionTriggerDirective.prototype.onChange; /** @type {?} */ NzMentionTriggerDirective.prototype.onTouched; /** @type {?} */ NzMentionTriggerDirective.prototype.onFocusin; /** @type {?} */ NzMentionTriggerDirective.prototype.onBlur; /** @type {?} */ NzMentionTriggerDirective.prototype.onInput; /** @type {?} */ NzMentionTriggerDirective.prototype.onKeydown; /** @type {?} */ NzMentionTriggerDirective.prototype.onClick; /** @type {?} */ NzMentionTriggerDirective.prototype.value; /** @type {?} */ NzMentionTriggerDirective.prototype.el; /** * @type {?} * @private */ NzMentionTriggerDirective.prototype.nzMentionService; } /** * @fileoverview added by tsickle * @suppress {checkTypes,constantProperty,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc */ /** * @record */ function MentionOnSearchTypes() { } if (false) { /** @type {?} */ MentionOnSearchTypes.prototype.value; /** @type {?} */ MentionOnSearchTypes.prototype.prefix; } /** * @record */ function Mention() { } if (false) { /** @type {?} */ Mention.prototype.startPos; /** @type {?} */ Mention.prototype.endPos; /** @type {?} */ Mention.prototype.mention; } class NzMentionComponent { /** * @param {?} ngDocument * @param {?} changeDetectorRef * @param {?} overlay * @param {?} viewContainerRef * @param {?} nzMentionService */ constructor(ngDocument, // tslint:disable-line:no-any changeDetectorRef, overlay, viewContainerRef, nzMentionService) { this.ngDocument = ngDocument; this.changeDetectorRef = changeDetectorRef; this.overlay = overlay; this.viewContainerRef = viewContainerRef; this.nzMentionService = nzMentionService; this.nzValueWith = (/** * @param {?} value * @return {?} */ value => value); // tslint:disable-line:no-any // tslint:disable-line:no-any this.nzPrefix = '@'; this.nzLoading = false; this.nzNotFoundContent = '无匹配结果,轻敲空格完成输入'; this.nzPlacement = 'bottom'; this.nzSuggestions = []; this.nzOnSelect = new EventEmitter(); this.nzOnSearchChange = new EventEmitter(); this.isOpen = false; this.filteredSuggestions = []; this.suggestionTemplate = null; // tslint:disable-line:no-any // tslint:disable-line:no-any this.activeIndex = -1; this.previousValue = null; } /** * @param {?} value * @return {?} */ set suggestionChild(value) { if (value) { this.suggestionTemplate = value; } } /** * @private * @return {?} */ get triggerNativeElement() { return this.trigger.el.nativeElement; } /** * @return {?} */ ngOnInit() { this.nzMentionService.triggerChanged().subscribe((/** * @param {?} trigger * @return {?} */ trigger => { this.trigger = trigger; this.bindTriggerEvents(); this.closeDropdown(); this.overlayRef = null; })); } /** * @param {?} changes * @return {?} */ ngOnChanges(changes) { if (changes.hasOwnProperty('nzSuggestions')) { if (this.isOpen) { this.previousValue = null; this.activeIndex = -1; this.resetDropdown(false); } } } /** * @return {?} */ ngOnDestroy() { this.closeDropdown(); } /** * @return {?} */ closeDropdown() { if (this.overlayRef && this.overlayRef.hasAttached()) { this.overlayRef.detach(); this.overlayBackdropClickSubscription.unsubscribe(); this.isOpen = false; this.changeDetectorRef.markForCheck(); } } /** * @return {?} */ openDropdown() { this.attachOverlay(); this.isOpen = true; this.changeDetectorRef.markForCheck(); } /** * @return {?} */ getMentions() { return this.trigger ? getMentions(this.trigger.value, this.nzPrefix) : []; } /** * @param {?} suggestion * @return {?} */ selectSuggestion(suggestion) { /** @type {?} */ const value = this.nzValueWith(suggestion); this.trigger.insertMention({ mention: value, startPos: this.cursorMentionStart, endPos: this.cursorMentionEnd }); this.nzOnSelect.emit(suggestion); this.closeDropdown(); this.activeIndex = -1; } /** * @private * @param {?} event * @return {?} */ handleInput(event) { /** @type {?} */ const target = (/** @type {?} */ (event.target)); this.trigger.onChange(target.value); this.trigger.value = target.value; this.resetDropdown(); } /** * @private * @param {?} event * @return {?} */ handleKeydown(event) { /** @type {?} */ const keyCode = event.keyCode; if (this.isOpen && keyCode === ENTER && this.activeIndex !== -1 && this.filteredSuggestions.length) { this.selectSuggestion(this.filteredSuggestions[this.activeIndex]); event.preventDefault(); } else if (keyCode === LEFT_ARROW || keyCode === RIGHT_ARROW) { this.resetDropdown(); event.stopPropagation(); } else { if (this.isOpen && (keyCode === TAB || keyCode === ESCAPE)) { this.closeDropdown(); return; } if (this.isOpen && keyCode === UP_ARROW) { this.setPreviousItemActive(); event.preventDefault(); event.stopPropagation(); } if (this.isOpen && keyCode === DOWN_ARROW) { this.setNextItemActive(); event.preventDefault(); event.stopPropagation(); } } } /** * @private * @return {?} */ handleClick() { this.resetDropdown(); } /** * @private * @return {?} */ bindTriggerEvents() { this.trigger.onInput.subscribe((/** * @param {?} e * @return {?} */ (e) => this.handleInput(e))); this.trigger.onKeydown.subscribe((/** * @param {?} e * @return {?} */ (e) => this.handleKeydown(e))); this.trigger.onClick.subscribe((/** * @return {?} */ () => this.handleClick())); } /** * @private * @param {?} value * @param {?} emit * @return {?} */ suggestionsFilter(value, emit) { /** @type {?} */ const suggestions = value.substring(1); if (this.previousValue === value) { return; } this.previousValue = value; if (emit) { this.nzOnSearchChange.emit({ value: (/** @type {?} */ (this.cursorMention)).substring(1), prefix: (/** @type {?} */ (this.cursorMention))[0] }); } /** @type {?} */ const searchValue = suggestions.toLowerCase(); this.filteredSuggestions = this.nzSuggestions.filter((/** * @param {?} suggestion * @return {?} */ suggestion => this.nzValueWith(suggestion) .toLowerCase() .includes(searchValue))); } /** * @private * @param {?=} emit * @return {?} */ resetDropdown(emit = true) { this.resetCursorMention(); if (typeof this.cursorMention !== 'string' || !this.canOpen()) { this.closeDropdown(); return; } this.suggestionsFilter(this.cursorMention, emit); /** @type {?} */ const activeIndex = this.filteredSuggestions.indexOf(this.cursorMention.substring(1)); this.activeIndex = activeIndex >= 0 ? activeIndex : 0; this.openDropdown(); } /** * @private * @return {?} */ setNextItemActive() { this.activeIndex = this.activeIndex + 1 <= this.filteredSuggestions.length - 1 ? this.activeIndex + 1 : 0; this.changeDetectorRef.markForCheck(); } /** * @private * @return {?} */ setPreviousItemActive() { this.activeIndex = this.activeIndex - 1 < 0 ? this.filteredSuggestions.length - 1 : this.activeIndex - 1; this.changeDetectorRef.markForCheck(); } /** * @private * @return {?} */ canOpen() { /** @type {?} */ const element = this.triggerNativeElement; return !element.readOnly && !element.disabled; } /** * @private * @return {?} */ resetCursorMention() { /** @type {?} */ const value = this.triggerNativeElement.value.replace(/[\r\n]/g, ' ') || ''; /** @type {?} */ const selectionStart = (/** @type {?} */ (this.triggerNativeElement.selectionStart)); /** @type {?} */ const prefix = typeof this.nzPrefix === 'string' ? [this.nzPrefix] : this.nzPrefix; /** @type {?} */ let i = prefix.length; while (i >= 0) { /** @type {?} */ const startPos = value.lastIndexOf(prefix[i], selectionStart); /** @type {?} */ const endPos = value.indexOf(' ', selectionStart) > -1 ? value.indexOf(' ', selectionStart) : value.length; /** @type {?} */ const mention = value.substring(startPos, endPos); if ((startPos > 0 && value[startPos - 1] !== ' ') || startPos < 0 || mention.includes(prefix[i], 1) || mention.includes(' ')) { this.cursorMention = null; this.cursorMentionStart = -1; this.cursorMentionEnd = -1; } else { this.cursorMention = mention; this.cursorMentionStart = startPos; this.cursorMentionEnd = endPos; return; } i--; } } /** * @private * @return {?} */ updatePositions() { /** @type {?} */ const coordinates = getCaretCoordinates(this.triggerNativeElement, this.cursorMentionStart); /** @type {?} */ const top = coordinates.top - this.triggerNativeElement.getBoundingClientRect().height - this.triggerNativeElement.scrollTop + (this.nzPlacement === 'bottom' ? coordinates.height - 6 : -6); /** @type {?} */ const left = coordinates.left - this.triggerNativeElement.scrollLeft; this.positionStrategy.withDefaultOffsetX(left).withDefaultOffsetY(top); if (this.nzPlacement === 'bottom') { this.positionStrategy.withPositions([...DEFAULT_MENTION_BOTTOM_POSITIONS]); } if (this.nzPlacement === 'top') { this.positionStrategy.withPositions([...DEFAULT_MENTION_TOP_POSITIONS]); } this.positionStrategy.apply(); } /** * @private * @return {?} */ subscribeOverlayBackdropClick() { return merge(fromEvent(this.ngDocument, 'click'), fromEvent(this.ngDocument, 'touchend')).subscribe((/** * @param {?} event * @return {?} */ (event) => { /** @type {?} */ const clickTarget = (/** @type {?} */ (event.target)); if (clickTarget !== this.trigger.el.nativeElement && this.isOpen) { this.closeDropdown(); } })); } /** * @private * @return {?} */ attachOverlay() { if (!this.overlayRef) { this.portal = new TemplatePortal(this.suggestionsTemp, this.viewContainerRef); this.overlayRef = this.overlay.create(this.getOverlayConfig()); } if (this.overlayRef && !this.overlayRef.hasAttached()) { this.overlayRef.attach(this.portal); this.overlayBackdropClickSubscription = this.subscribeOverlayBackdropClick(); } this.updatePositions(); } /** * @private * @return {?} */ getOverlayConfig() { return new OverlayConfig({ positionStrategy: this.getOverlayPosition(), scrollStrategy: this.overlay.scrollStrategies.reposition() }); } /** * @private * @return {?} */ getOverlayPosition() { /** @type {?} */ const positions = [ new ConnectionPositionPair({ originX: 'start', originY: 'bottom' }, { overlayX: 'start', overlayY: 'top' }), new ConnectionPositionPair({ originX: 'start', originY: 'top' }, { overlayX: 'start', overlayY: 'bottom' }) ]; this.positionStrategy = this.overlay .position() .flexibleConnectedTo(this.trigger.el) .withPositions(positions) .withFlexibleDimensions(false) .withPush(false); return this.positionStrategy; } } NzMentionComponent.decorators = [ { type: Component, args: [{ selector: 'nz-mention', exportAs: 'nzMention', template: "<ng-content></ng-content>\n<ng-template #suggestions>\n <ul class=\"ant-mention-dropdown\">\n <li class=\"ant-mention-dropdown-item\"\n *ngFor=\"let suggestion of filteredSuggestions; let i = index\"\n [class.focus]=\"i === activeIndex\"\n (mousedown)=\"$event.preventDefault()\"\n (click)=\"selectSuggestion(suggestion)\">\n <ng-container *ngIf=\"suggestionTemplate else defaultSuggestion\">\n <ng-container *ngTemplateOutlet=\"suggestionTemplate; context: {$implicit: suggestion}\"></ng-container>\n </ng-container>\n <ng-template #defaultSuggestion>{{ nzValueWith(suggestion) }}</ng-template>\n </li>\n <li class=\"ant-mention-dropdown-notfound ant-mention-dropdown-item\"\n *ngIf=\"filteredSuggestions.length === 0\">\n <span *ngIf=\"nzLoading\"><i nz-icon nzType=\"loading\"></i></span>\n <span *ngIf=\"!nzLoading\">{{ nzNotFoundContent }}</span>\n </li>\n </ul>\n</ng-template>\n", preserveWhitespaces: false, changeDetection: ChangeDetectionStrategy.OnPush, providers: [NzMentionService], styles: [` .ant-mention-dropdown { top: 100%; left: 0; position: relative; width: 100%; margin-top: 4px; margin-bottom: 4px; } `] }] } ]; /** @nocollapse */ NzMentionComponent.ctorParameters = () => [ { type: undefined, decorators: [{ type: Optional }, { type: Inject, args: [DOCUMENT,] }] }, { type: ChangeDetectorRef }, { type: Overlay }, { type: ViewContainerRef }, { type: NzMentionService } ]; NzMentionComponent.propDecorators = { nzValueWith: [{ type: Input }], nzPrefix: [{ type: Input }], nzLoading: [{ type: Input }], nzNotFoundContent: [{ type: Input }], nzPlacement: [{ type: Input }], nzSuggestions: [{ type: Input }], nzOnSelect: [{ type: Output }], nzOnSearchChange: [{ type: Output }], suggestionsTemp: [{ type: ViewChild, args: [TemplateRef, { static: false },] }], suggestionChild: [{ type: ContentChild, args: [NzMentionSuggestionDirective, { static: false, read: TemplateRef },] }] }; __decorate([ InputBoolean(), __metadata("design:type", Object) ], NzMentionComponent.prototype, "nzLoading", void 0); if (false) { /** @type {?} */ NzMentionComponent.prototype.nzValueWith; /** @type {?} */ NzMentionComponent.prototype.nzPrefix; /** @type {?} */ NzMentionComponent.prototype.nzLoading; /** @type {?} */ NzMentionComponent.prototype.nzNotFoundContent; /** @type {?} */ NzMentionComponent.prototype.nzPlacement; /** @type {?} */ NzMentionComponent.prototype.nzSuggestions; /** @type {?} */ NzMentionComponent.prototype.nzOnSelect; /** @type {?} */ NzMentionComponent.prototype.nzOnSearchChange; /** @type {?} */ NzMentionComponent.prototype.trigger; /** @type {?} */ NzMentionComponent.prototype.suggestionsTemp; /** @type {?} */ NzMentionComponent.prototype.isOpen; /** @type {?} */ NzMentionComponent.prototype.filteredSuggestions; /** @type {?} */ NzMentionComponent.prototype.suggestionTemplate; /** @type {?} */ NzMentionComponent.prototype.activeIndex; /** * @type {?} * @private */ NzMentionComponent.prototype.previousValue; /** * @type {?} * @private */ NzMentionComponent.prototype.cursorMention; /** * @type {?} * @private */ NzMentionComponent.prototype.cursorMentionStart; /** * @type {?} * @private */ NzMentionComponent.prototype.cursorMentionEnd; /** * @type {?} * @private */ NzMentionComponent.prototype.overlayRef; /** * @type {?} * @private */ NzMentionComponent.prototype.portal; /** * @type {?} * @private */ NzMentionComponent.prototype.positionStrategy; /** * @type {?} * @private */ NzMentionComponent.prototype.overlayBackdropClickSubscription; /** * @type {?} * @private */ NzMentionComponent.prototype.ngDocument; /** * @type {?} * @private */ NzMentionComponent.prototype.changeDetectorRef; /** * @type {?} * @private */ NzMentionComponent.prototype.overlay; /** * @type {?} * @private */ NzMentionComponent.prototype.viewContainerRef; /** * @type {?} * @private */ NzMentionComponent.prototype.nzMentionService; } /** * @fileoverview added by tsickle * @suppress {checkTypes,constantProperty,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc */ /** @type {?} */ const COMPONENTS = [NzMentionComponent, NzMentionTriggerDirective, NzMentionSuggestionDirective]; class NzMentionModule { } NzMentionModule.decorators = [ { type: NgModule, args: [{ imports: [CommonModule, FormsModule, OverlayModule, NzIconModule], declarations: [...COMPONENTS], exports: [...COMPONENTS] },] } ]; /** * @fileoverview added by tsickle * @suppress {checkTypes,constantProperty,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc */ /** * @fileoverview added by tsickle * @suppress {checkTypes,constantProperty,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc */ export { NZ_MENTION_TRIGGER_ACCESSOR, NzMentionComponent, NzMentionModule, NzMentionService, NzMentionSuggestionDirective, NzMentionTriggerDirective }; //# sourceMappingURL=ng-zorro-antd-mention.js.map