ng-zorro-antd
Version:
An enterprise-class UI components based on Ant Design and Angular
498 lines (491 loc) • 19.2 kB
JavaScript
import { EventEmitter, Directive, ElementRef, ViewContainerRef, ComponentFactoryResolver, Renderer2, ChangeDetectorRef, Optional, ViewChild, TemplateRef, Host, Input, Output, Component, ChangeDetectionStrategy, ViewEncapsulation, NgModule } from '@angular/core';
import { zoomBigMotion } from 'ng-zorro-antd/core/animation';
import { isPresetColor } from 'ng-zorro-antd/core/color';
import { NzNoAnimationDirective, NzNoAnimationModule } from 'ng-zorro-antd/core/no-animation';
import { Directionality, BidiModule } from '@angular/cdk/bidi';
import { OverlayModule } from '@angular/cdk/overlay';
import { DEFAULT_TOOLTIP_POSITIONS, POSITION_MAP, getPlacementName, NzOverlayModule } from 'ng-zorro-antd/core/overlay';
import { toBoolean, isNotNil } from 'ng-zorro-antd/core/util';
import { Subject } from 'rxjs';
import { distinctUntilChanged, takeUntil } from 'rxjs/operators';
import { CommonModule } from '@angular/common';
import { NzOutletModule } from 'ng-zorro-antd/core/outlet';
/**
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://github.com/NG-ZORRO/ng-zorro-antd/blob/master/LICENSE
*/
class NzTooltipBaseDirective {
constructor(elementRef, hostView, resolver, renderer, noAnimation) {
this.elementRef = elementRef;
this.hostView = hostView;
this.resolver = resolver;
this.renderer = renderer;
this.noAnimation = noAnimation;
this.visibleChange = new EventEmitter();
this.internalVisible = false;
this.destroy$ = new Subject();
this.triggerDisposables = [];
}
/**
* This true title that would be used in other parts on this component.
*/
get _title() {
return this.title || this.directiveTitle || null;
}
get _content() {
return this.content || this.directiveContent || null;
}
get _trigger() {
return typeof this.trigger !== 'undefined' ? this.trigger : 'hover';
}
get _placement() {
const p = this.placement;
return Array.isArray(p) && p.length > 0 ? p : typeof p === 'string' && p ? [p] : ['top'];
}
get _visible() {
return (typeof this.visible !== 'undefined' ? this.visible : this.internalVisible) || false;
}
get _mouseEnterDelay() {
return this.mouseEnterDelay || 0.15;
}
get _mouseLeaveDelay() {
return this.mouseLeaveDelay || 0.1;
}
get _overlayClassName() {
return this.overlayClassName || null;
}
get _overlayStyle() {
return this.overlayStyle || null;
}
getProxyPropertyMap() {
return {
noAnimation: ['noAnimation', () => this.noAnimation]
};
}
ngOnChanges(changes) {
const { specificTrigger } = changes;
if (specificTrigger && !specificTrigger.isFirstChange()) {
this.registerTriggers();
}
if (this.component) {
this.updatePropertiesByChanges(changes);
}
}
ngAfterViewInit() {
this.createComponent();
this.registerTriggers();
}
ngOnDestroy() {
this.destroy$.next();
this.destroy$.complete();
// Clear toggling timer. Issue #3875 #4317 #4386
this.clearTogglingTimer();
this.removeTriggerListeners();
}
show() {
var _a;
(_a = this.component) === null || _a === void 0 ? void 0 : _a.show();
}
hide() {
var _a;
(_a = this.component) === null || _a === void 0 ? void 0 : _a.hide();
}
/**
* Force the component to update its position.
*/
updatePosition() {
if (this.component) {
this.component.updatePosition();
}
}
/**
* Create a dynamic tooltip component. This method can be override.
*/
createComponent() {
const componentRef = this.hostView.createComponent(this.componentFactory);
this.component = componentRef.instance;
// Remove the component's DOM because it should be in the overlay container.
this.renderer.removeChild(this.renderer.parentNode(this.elementRef.nativeElement), componentRef.location.nativeElement);
this.component.setOverlayOrigin({ elementRef: this.origin || this.elementRef });
this.initProperties();
this.component.nzVisibleChange.pipe(distinctUntilChanged(), takeUntil(this.destroy$)).subscribe((visible) => {
this.internalVisible = visible;
this.visibleChange.emit(visible);
});
}
registerTriggers() {
// When the method gets invoked, all properties has been synced to the dynamic component.
// After removing the old API, we can just check the directive's own `nzTrigger`.
const el = this.elementRef.nativeElement;
const trigger = this.trigger;
this.removeTriggerListeners();
if (trigger === 'hover') {
let overlayElement;
this.triggerDisposables.push(this.renderer.listen(el, 'mouseenter', () => {
this.delayEnterLeave(true, true, this._mouseEnterDelay);
}));
this.triggerDisposables.push(this.renderer.listen(el, 'mouseleave', () => {
var _a;
this.delayEnterLeave(true, false, this._mouseLeaveDelay);
if (((_a = this.component) === null || _a === void 0 ? void 0 : _a.overlay.overlayRef) && !overlayElement) {
overlayElement = this.component.overlay.overlayRef.overlayElement;
this.triggerDisposables.push(this.renderer.listen(overlayElement, 'mouseenter', () => {
this.delayEnterLeave(false, true, this._mouseEnterDelay);
}));
this.triggerDisposables.push(this.renderer.listen(overlayElement, 'mouseleave', () => {
this.delayEnterLeave(false, false, this._mouseLeaveDelay);
}));
}
}));
}
else if (trigger === 'focus') {
this.triggerDisposables.push(this.renderer.listen(el, 'focus', () => this.show()));
this.triggerDisposables.push(this.renderer.listen(el, 'blur', () => this.hide()));
}
else if (trigger === 'click') {
this.triggerDisposables.push(this.renderer.listen(el, 'click', (e) => {
e.preventDefault();
this.show();
}));
}
// Else do nothing because user wants to control the visibility programmatically.
}
updatePropertiesByChanges(changes) {
this.updatePropertiesByKeys(Object.keys(changes));
}
updatePropertiesByKeys(keys) {
var _a;
const mappingProperties = Object.assign({
// common mappings
title: ['nzTitle', () => this._title], directiveTitle: ['nzTitle', () => this._title], content: ['nzContent', () => this._content], directiveContent: ['nzContent', () => this._content], trigger: ['nzTrigger', () => this._trigger], placement: ['nzPlacement', () => this._placement], visible: ['nzVisible', () => this._visible], mouseEnterDelay: ['nzMouseEnterDelay', () => this._mouseEnterDelay], mouseLeaveDelay: ['nzMouseLeaveDelay', () => this._mouseLeaveDelay], overlayClassName: ['nzOverlayClassName', () => this._overlayClassName], overlayStyle: ['nzOverlayStyle', () => this._overlayStyle] }, this.getProxyPropertyMap());
(keys || Object.keys(mappingProperties).filter(key => !key.startsWith('directive'))).forEach((property) => {
if (mappingProperties[property]) {
const [name, valueFn] = mappingProperties[property];
this.updateComponentValue(name, valueFn());
}
});
(_a = this.component) === null || _a === void 0 ? void 0 : _a.updateByDirective();
}
initProperties() {
this.updatePropertiesByKeys();
}
updateComponentValue(key, value) {
if (typeof value !== 'undefined') {
// @ts-ignore
this.component[key] = value;
}
}
delayEnterLeave(isOrigin, isEnter, delay = -1) {
if (this.delayTimer) {
this.clearTogglingTimer();
}
else if (delay > 0) {
this.delayTimer = setTimeout(() => {
this.delayTimer = undefined;
isEnter ? this.show() : this.hide();
}, delay * 1000);
}
else {
// `isOrigin` is used due to the tooltip will not hide immediately
// (may caused by the fade-out animation).
isEnter && isOrigin ? this.show() : this.hide();
}
}
removeTriggerListeners() {
this.triggerDisposables.forEach(dispose => dispose());
this.triggerDisposables.length = 0;
}
clearTogglingTimer() {
if (this.delayTimer) {
clearTimeout(this.delayTimer);
this.delayTimer = undefined;
}
}
}
NzTooltipBaseDirective.decorators = [
{ type: Directive }
];
NzTooltipBaseDirective.ctorParameters = () => [
{ type: ElementRef },
{ type: ViewContainerRef },
{ type: ComponentFactoryResolver },
{ type: Renderer2 },
{ type: NzNoAnimationDirective }
];
// tslint:disable-next-line:directive-class-suffix
class NzTooltipBaseComponent {
constructor(cdr, directionality, noAnimation) {
this.cdr = cdr;
this.directionality = directionality;
this.noAnimation = noAnimation;
this.nzTitle = null;
this.nzContent = null;
this.nzOverlayStyle = {};
this.nzVisibleChange = new Subject();
this._visible = false;
this._trigger = 'hover';
this.preferredPlacement = 'top';
this.dir = 'ltr';
this._classMap = {};
this._hasBackdrop = false;
this._prefix = 'ant-tooltip';
this._positions = [...DEFAULT_TOOLTIP_POSITIONS];
this.destroy$ = new Subject();
}
set nzVisible(value) {
const visible = toBoolean(value);
if (this._visible !== visible) {
this._visible = visible;
this.nzVisibleChange.next(visible);
}
}
get nzVisible() {
return this._visible;
}
set nzTrigger(value) {
this._trigger = value;
}
get nzTrigger() {
return this._trigger;
}
set nzPlacement(value) {
const preferredPosition = value.map(placement => POSITION_MAP[placement]);
this._positions = [...preferredPosition, ...DEFAULT_TOOLTIP_POSITIONS];
}
ngOnInit() {
var _a;
(_a = this.directionality.change) === null || _a === void 0 ? void 0 : _a.pipe(takeUntil(this.destroy$)).subscribe((direction) => {
this.dir = direction;
this.cdr.detectChanges();
});
this.dir = this.directionality.value;
}
ngOnDestroy() {
this.nzVisibleChange.complete();
this.destroy$.next();
this.destroy$.complete();
}
show() {
if (this.nzVisible) {
return;
}
if (!this.isEmpty()) {
this.nzVisible = true;
this.nzVisibleChange.next(true);
this.cdr.detectChanges();
}
// for ltr for overlay to display tooltip in correct placement in rtl direction.
if (this.origin && this.overlay && this.overlay.overlayRef && this.overlay.overlayRef.getDirection() === 'rtl') {
this.overlay.overlayRef.setDirection('ltr');
}
}
hide() {
if (!this.nzVisible) {
return;
}
this.nzVisible = false;
this.nzVisibleChange.next(false);
this.cdr.detectChanges();
}
updateByDirective() {
this.updateStyles();
this.cdr.detectChanges();
Promise.resolve().then(() => {
this.updatePosition();
this.updateVisibilityByTitle();
});
}
/**
* Force the component to update its position.
*/
updatePosition() {
if (this.origin && this.overlay && this.overlay.overlayRef) {
this.overlay.overlayRef.updatePosition();
}
}
onPositionChange(position) {
this.preferredPlacement = getPlacementName(position);
this.updateStyles();
// We have to trigger immediate change detection or the element would blink.
this.cdr.detectChanges();
}
updateStyles() {
this._classMap = {
[this.nzOverlayClassName]: true,
[`${this._prefix}-placement-${this.preferredPlacement}`]: true
};
}
setOverlayOrigin(origin) {
this.origin = origin;
this.cdr.markForCheck();
}
onClickOutside(event) {
if (!this.origin.elementRef.nativeElement.contains(event.target)) {
this.hide();
}
}
/**
* Hide the component while the content is empty.
*/
updateVisibilityByTitle() {
if (this.isEmpty()) {
this.hide();
}
}
}
NzTooltipBaseComponent.decorators = [
{ type: Directive }
];
NzTooltipBaseComponent.ctorParameters = () => [
{ type: ChangeDetectorRef },
{ type: Directionality, decorators: [{ type: Optional }] },
{ type: NzNoAnimationDirective }
];
NzTooltipBaseComponent.propDecorators = {
overlay: [{ type: ViewChild, args: ['overlay', { static: false },] }]
};
function isTooltipEmpty(value) {
return value instanceof TemplateRef ? false : value === '' || !isNotNil(value);
}
/**
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://github.com/NG-ZORRO/ng-zorro-antd/blob/master/LICENSE
*/
class NzTooltipDirective extends NzTooltipBaseDirective {
constructor(elementRef, hostView, resolver, renderer, noAnimation) {
super(elementRef, hostView, resolver, renderer, noAnimation);
this.trigger = 'hover';
this.placement = 'top';
// tslint:disable-next-line:no-output-rename
this.visibleChange = new EventEmitter();
this.componentFactory = this.resolver.resolveComponentFactory(NzToolTipComponent);
}
getProxyPropertyMap() {
return {
nzTooltipColor: ['nzColor', () => this.nzTooltipColor]
};
}
}
NzTooltipDirective.decorators = [
{ type: Directive, args: [{
selector: '[nz-tooltip]',
exportAs: 'nzTooltip',
host: {
'[class.ant-tooltip-open]': 'visible'
}
},] }
];
NzTooltipDirective.ctorParameters = () => [
{ type: ElementRef },
{ type: ViewContainerRef },
{ type: ComponentFactoryResolver },
{ type: Renderer2 },
{ type: NzNoAnimationDirective, decorators: [{ type: Host }, { type: Optional }] }
];
NzTooltipDirective.propDecorators = {
title: [{ type: Input, args: ['nzTooltipTitle',] }],
directiveTitle: [{ type: Input, args: ['nz-tooltip',] }],
trigger: [{ type: Input, args: ['nzTooltipTrigger',] }],
placement: [{ type: Input, args: ['nzTooltipPlacement',] }],
origin: [{ type: Input, args: ['nzTooltipOrigin',] }],
visible: [{ type: Input, args: ['nzTooltipVisible',] }],
mouseEnterDelay: [{ type: Input, args: ['nzTooltipMouseEnterDelay',] }],
mouseLeaveDelay: [{ type: Input, args: ['nzTooltipMouseLeaveDelay',] }],
overlayClassName: [{ type: Input, args: ['nzTooltipOverlayClassName',] }],
overlayStyle: [{ type: Input, args: ['nzTooltipOverlayStyle',] }],
nzTooltipColor: [{ type: Input }],
visibleChange: [{ type: Output, args: ['nzTooltipVisibleChange',] }]
};
class NzToolTipComponent extends NzTooltipBaseComponent {
constructor(cdr, directionality, noAnimation) {
super(cdr, directionality, noAnimation);
this.noAnimation = noAnimation;
this.nzTitle = null;
this._contentStyleMap = {};
}
isEmpty() {
return isTooltipEmpty(this.nzTitle);
}
updateStyles() {
const isColorPreset = this.nzColor && isPresetColor(this.nzColor);
this._classMap = {
[this.nzOverlayClassName]: true,
[`${this._prefix}-placement-${this.preferredPlacement}`]: true,
[`${this._prefix}-${this.nzColor}`]: isColorPreset
};
this._contentStyleMap = {
backgroundColor: !!this.nzColor && !isColorPreset ? this.nzColor : null
};
}
}
NzToolTipComponent.decorators = [
{ type: Component, args: [{
selector: 'nz-tooltip',
exportAs: 'nzTooltipComponent',
changeDetection: ChangeDetectionStrategy.OnPush,
encapsulation: ViewEncapsulation.None,
animations: [zoomBigMotion],
template: `
<ng-template
#overlay="cdkConnectedOverlay"
cdkConnectedOverlay
nzConnectedOverlay
[cdkConnectedOverlayOrigin]="origin"
[cdkConnectedOverlayOpen]="_visible"
[cdkConnectedOverlayPositions]="_positions"
[cdkConnectedOverlayPush]="true"
(overlayOutsideClick)="onClickOutside($event)"
(detach)="hide()"
(positionChange)="onPositionChange($event)"
>
<div
class="ant-tooltip"
[class.ant-tooltip-rtl]="dir === 'rtl'"
[ngClass]="_classMap"
[ngStyle]="nzOverlayStyle"
[@.disabled]="noAnimation?.nzNoAnimation"
[nzNoAnimation]="noAnimation?.nzNoAnimation"
[ ]="'active'"
>
<div class="ant-tooltip-content">
<div class="ant-tooltip-arrow">
<span class="ant-tooltip-arrow-content" [ngStyle]="_contentStyleMap"></span>
</div>
<div class="ant-tooltip-inner" [ngStyle]="_contentStyleMap">
<ng-container *nzStringTemplateOutlet="nzTitle">{{ nzTitle }}</ng-container>
</div>
</div>
</div>
</ng-template>
`,
preserveWhitespaces: false
},] }
];
NzToolTipComponent.ctorParameters = () => [
{ type: ChangeDetectorRef },
{ type: Directionality, decorators: [{ type: Optional }] },
{ type: NzNoAnimationDirective, decorators: [{ type: Host }, { type: Optional }] }
];
/**
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://github.com/NG-ZORRO/ng-zorro-antd/blob/master/LICENSE
*/
class NzToolTipModule {
}
NzToolTipModule.decorators = [
{ type: NgModule, args: [{
declarations: [NzToolTipComponent, NzTooltipDirective],
exports: [NzToolTipComponent, NzTooltipDirective],
entryComponents: [NzToolTipComponent],
imports: [BidiModule, CommonModule, OverlayModule, NzOutletModule, NzOverlayModule, NzNoAnimationModule]
},] }
];
/**
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://github.com/NG-ZORRO/ng-zorro-antd/blob/master/LICENSE
*/
/**
* Generated bundle index. Do not edit.
*/
export { NzToolTipComponent, NzToolTipModule, NzTooltipBaseComponent, NzTooltipBaseDirective, NzTooltipDirective, isTooltipEmpty };
//# sourceMappingURL=ng-zorro-antd-tooltip.js.map