ng-zorro-antd
Version:
An enterprise-class UI components based on Ant Design and Angular
762 lines (749 loc) • 27.1 kB
JavaScript
import { __decorate, __metadata } from 'tslib';
import { Directionality, BidiModule } from '@angular/cdk/bidi';
import { Component, ChangeDetectionStrategy, ViewEncapsulation, EventEmitter, ChangeDetectorRef, ViewChild, Injector, Injectable, Optional, Directive, ElementRef, Input, NgModule } from '@angular/core';
import { NzConfigService, WithConfig } from 'ng-zorro-antd/core/config';
import { isNotNil, InputBoolean } from 'ng-zorro-antd/core/util';
import { Subject } from 'rxjs';
import { filter, take, takeUntil } from 'rxjs/operators';
import { OverlayRef, OverlayConfig, Overlay, OverlayModule } from '@angular/cdk/overlay';
import { ComponentPortal, PortalModule } from '@angular/cdk/portal';
import { ESCAPE, hasModifierKey } from '@angular/cdk/keycodes';
import { fadeMotion } from 'ng-zorro-antd/core/animation';
import { DragDropModule } from '@angular/cdk/drag-drop';
import { CommonModule } from '@angular/common';
import { NzIconModule } from 'ng-zorro-antd/icon';
import { NzPipesModule } from 'ng-zorro-antd/pipes';
/**
* 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 NzImageGroupComponent {
constructor() {
this.images = [];
}
addImage(image) {
this.images.push(image);
}
}
NzImageGroupComponent.decorators = [
{ type: Component, args: [{
selector: 'nz-image-group',
exportAs: 'nzImageGroup',
template: '<ng-content></ng-content>',
preserveWhitespaces: false,
changeDetection: ChangeDetectionStrategy.OnPush,
encapsulation: ViewEncapsulation.None
},] }
];
const FADE_CLASS_NAME_MAP = {
enter: 'fade-enter',
enterActive: 'fade-enter-active',
leave: 'fade-leave',
leaveActive: 'fade-leave-active'
};
const IMAGE_PREVIEW_MASK_CLASS_NAME = 'ant-image-preview-mask';
const NZ_CONFIG_MODULE_NAME = 'image';
class NzImagePreviewOptions {
constructor() {
this.nzKeyboard = true;
this.nzNoAnimation = false;
this.nzMaskClosable = true;
this.nzCloseOnNavigation = true;
}
}
/**
* 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 NzImagePreviewRef {
constructor(previewInstance, config, overlayRef) {
this.previewInstance = previewInstance;
this.config = config;
this.overlayRef = overlayRef;
overlayRef
.keydownEvents()
.pipe(filter(event => {
return this.config.nzKeyboard && event.keyCode === ESCAPE && !hasModifierKey(event);
}))
.subscribe(event => {
event.preventDefault();
this.close();
});
overlayRef.detachments().subscribe(() => {
this.overlayRef.dispose();
});
previewInstance.containerClick.pipe(take(1)).subscribe(() => {
this.close();
});
previewInstance.closeClick.pipe(take(1)).subscribe(() => {
this.close();
});
previewInstance.animationStateChanged
.pipe(filter(event => event.phaseName === 'done' && event.toState === 'leave'), take(1))
.subscribe(() => {
this.dispose();
});
}
switchTo(index) {
this.previewInstance.switchTo(index);
}
next() {
this.previewInstance.next();
}
prev() {
this.previewInstance.prev();
}
close() {
this.previewInstance.startLeaveAnimation();
}
dispose() {
this.overlayRef.dispose();
}
}
/**
* 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
*/
/**
* fit content details: https://github.com/NG-ZORRO/ng-zorro-antd/pull/6154#issuecomment-745025554
*
* calc position x,y point
*
* CASE (width <= clientWidth && height <= clientHeight):
*
* ------------- clientWidth -------------
* | |
* | ------ width ------ |
* | | | |
* | | | |
* client height | |
* Height | | |
* | | | |
* | ------------------- |
* | |
* | |
* ---------------------------------------
* fixedPosition = { x: 0, y: 0 }
*
*
*
* CASE (width > clientWidth || height > clientHeight):
*
* ------------- clientWidth -------------
* | | |
* | top |
* | | |
* |--left--|--------------- width -----------------
* | | |
* client | |
* Height | |
* | | |
* | | |
* | height |
* | | |
* ---------| |
* | |
* | |
* | |
* ----------------------------------------
*
*
* - left || top > 0
* left -> 0 || top -> 0
*
* - (left + width) < clientWidth || (top + height) < clientHeight
* - left | top + width | height < clientWidth | clientHeight -> Back left | top + width | height === clientWidth | clientHeight
*
* DEFAULT:
* - hold position
*
*/
function getFitContentPosition(params) {
let fixPos = {};
if (params.width <= params.clientWidth && params.height <= params.clientHeight) {
fixPos = {
x: 0,
y: 0
};
}
if (params.width > params.clientWidth || params.height > params.clientHeight) {
fixPos = {
x: fitPoint(params.left, params.width, params.clientWidth),
y: fitPoint(params.top, params.height, params.clientHeight)
};
}
return fixPos;
}
function getOffset(node) {
const box = node.getBoundingClientRect();
const docElem = document.documentElement;
// use docElem.scrollLeft to support IE
return {
left: box.left + (window.pageXOffset || docElem.scrollLeft) - (docElem.clientLeft || document.body.clientLeft || 0),
top: box.top + (window.pageYOffset || docElem.scrollTop) - (docElem.clientTop || document.body.clientTop || 0)
};
}
function getClientSize() {
const width = document.documentElement.clientWidth;
const height = window.innerHeight || document.documentElement.clientHeight;
return {
width,
height
};
}
function fitPoint(start, size, clientSize) {
const startAddSize = start + size;
const offsetStart = (size - clientSize) / 2;
let distance = null;
if (size > clientSize) {
if (start > 0) {
distance = offsetStart;
}
if (start < 0 && startAddSize < clientSize) {
distance = -offsetStart;
}
}
else {
if (start < 0 || startAddSize > clientSize) {
distance = start < 0 ? offsetStart : -offsetStart;
}
}
return distance;
}
const initialPosition = {
x: 0,
y: 0
};
class NzImagePreviewComponent {
constructor(cdr, nzConfigService, config, overlayRef) {
var _a, _b;
this.cdr = cdr;
this.nzConfigService = nzConfigService;
this.config = config;
this.overlayRef = overlayRef;
this.images = [];
this.index = 0;
this.isDragging = false;
this.visible = true;
this.animationState = 'enter';
this.animationStateChanged = new EventEmitter();
this.previewImageTransform = '';
this.previewImageWrapperTransform = '';
this.operations = [
{
icon: 'close',
onClick: () => {
this.onClose();
},
type: 'close'
},
{
icon: 'zoom-in',
onClick: () => {
this.onZoomIn();
},
type: 'zoomIn'
},
{
icon: 'zoom-out',
onClick: () => {
this.onZoomOut();
},
type: 'zoomOut'
},
{
icon: 'rotate-right',
onClick: () => {
this.onRotateRight();
},
type: 'rotateRight'
},
{
icon: 'rotate-left',
onClick: () => {
this.onRotateLeft();
},
type: 'rotateLeft'
}
];
this.zoomOutDisabled = false;
this.position = Object.assign({}, initialPosition);
this.containerClick = new EventEmitter();
this.closeClick = new EventEmitter();
this.destroy$ = new Subject();
// TODO: move to host after View Engine deprecation
this.zoom = (_a = this.config.nzZoom) !== null && _a !== void 0 ? _a : 1;
this.rotate = (_b = this.config.nzRotate) !== null && _b !== void 0 ? _b : 0;
this.updateZoomOutDisabled();
this.updatePreviewImageTransform();
this.updatePreviewImageWrapperTransform();
}
get animationDisabled() {
var _a;
return (_a = this.config.nzNoAnimation) !== null && _a !== void 0 ? _a : false;
}
get maskClosable() {
var _a, _b;
const defaultConfig = this.nzConfigService.getConfigForComponent(NZ_CONFIG_MODULE_NAME) || {};
return (_b = (_a = this.config.nzMaskClosable) !== null && _a !== void 0 ? _a : defaultConfig.nzMaskClosable) !== null && _b !== void 0 ? _b : true;
}
setImages(images) {
this.images = images;
this.cdr.markForCheck();
}
switchTo(index) {
this.index = index;
this.cdr.markForCheck();
}
next() {
if (this.index < this.images.length - 1) {
this.reset();
this.index++;
this.updatePreviewImageTransform();
this.updatePreviewImageWrapperTransform();
this.updateZoomOutDisabled();
this.cdr.markForCheck();
}
}
prev() {
if (this.index > 0) {
this.reset();
this.index--;
this.updatePreviewImageTransform();
this.updatePreviewImageWrapperTransform();
this.updateZoomOutDisabled();
this.cdr.markForCheck();
}
}
markForCheck() {
this.cdr.markForCheck();
}
onClose() {
this.closeClick.emit();
}
onZoomIn() {
this.zoom += 1;
this.updatePreviewImageTransform();
this.updateZoomOutDisabled();
this.position = Object.assign({}, initialPosition);
}
onZoomOut() {
if (this.zoom > 1) {
this.zoom -= 1;
this.updatePreviewImageTransform();
this.updateZoomOutDisabled();
this.position = Object.assign({}, initialPosition);
}
}
onRotateRight() {
this.rotate += 90;
this.updatePreviewImageTransform();
}
onRotateLeft() {
this.rotate -= 90;
this.updatePreviewImageTransform();
}
onSwitchLeft(event) {
event.preventDefault();
event.stopPropagation();
this.prev();
}
onSwitchRight(event) {
event.preventDefault();
event.stopPropagation();
this.next();
}
onContainerClick(e) {
if (e.target === e.currentTarget && this.maskClosable) {
this.containerClick.emit();
}
}
onAnimationStart(event) {
if (event.toState === 'enter') {
this.setEnterAnimationClass();
}
else if (event.toState === 'leave') {
this.setLeaveAnimationClass();
}
this.animationStateChanged.emit(event);
}
onAnimationDone(event) {
if (event.toState === 'enter') {
this.setEnterAnimationClass();
}
else if (event.toState === 'leave') {
this.setLeaveAnimationClass();
}
this.animationStateChanged.emit(event);
}
startLeaveAnimation() {
this.animationState = 'leave';
this.cdr.markForCheck();
}
onDragStarted() {
this.isDragging = true;
}
onDragReleased() {
this.isDragging = false;
const width = this.imageRef.nativeElement.offsetWidth * this.zoom;
const height = this.imageRef.nativeElement.offsetHeight * this.zoom;
const { left, top } = getOffset(this.imageRef.nativeElement);
const { width: clientWidth, height: clientHeight } = getClientSize();
const isRotate = this.rotate % 180 !== 0;
const fitContentParams = {
width: isRotate ? height : width,
height: isRotate ? width : height,
left,
top,
clientWidth,
clientHeight
};
const fitContentPos = getFitContentPosition(fitContentParams);
if (isNotNil(fitContentPos.x) || isNotNil(fitContentPos.y)) {
this.position = Object.assign(Object.assign({}, this.position), fitContentPos);
}
}
ngOnDestroy() {
this.destroy$.next();
this.destroy$.complete();
}
updatePreviewImageTransform() {
this.previewImageTransform = `scale3d(${this.zoom}, ${this.zoom}, 1) rotate(${this.rotate}deg)`;
}
updatePreviewImageWrapperTransform() {
this.previewImageWrapperTransform = `translate3d(${this.position.x}px, ${this.position.y}px, 0)`;
}
updateZoomOutDisabled() {
this.zoomOutDisabled = this.zoom <= 1;
}
setEnterAnimationClass() {
if (this.animationDisabled) {
return;
}
const backdropElement = this.overlayRef.backdropElement;
if (backdropElement) {
backdropElement.classList.add(FADE_CLASS_NAME_MAP.enter);
backdropElement.classList.add(FADE_CLASS_NAME_MAP.enterActive);
}
}
setLeaveAnimationClass() {
if (this.animationDisabled) {
return;
}
const backdropElement = this.overlayRef.backdropElement;
if (backdropElement) {
backdropElement.classList.add(FADE_CLASS_NAME_MAP.leave);
backdropElement.classList.add(FADE_CLASS_NAME_MAP.leaveActive);
}
}
reset() {
this.zoom = 1;
this.rotate = 0;
this.position = Object.assign({}, initialPosition);
}
}
NzImagePreviewComponent.decorators = [
{ type: Component, args: [{
selector: 'nz-image-preview',
exportAs: 'nzImagePreview',
animations: [fadeMotion],
template: `
<div class="ant-image-preview">
<div tabindex="0" aria-hidden="true" style="width: 0; height: 0; overflow: hidden; outline: none;"></div>
<div class="ant-image-preview-content">
<div class="ant-image-preview-body">
<ul class="ant-image-preview-operations">
<li
class="ant-image-preview-operations-operation"
[class.ant-image-preview-operations-operation-disabled]="zoomOutDisabled && option.type === 'zoomOut'"
(click)="option.onClick()"
*ngFor="let option of operations"
>
<span class="ant-image-preview-operations-icon" nz-icon [nzType]="option.icon" nzTheme="outline"></span>
</li>
</ul>
<div
class="ant-image-preview-img-wrapper"
cdkDrag
[style.transform]="previewImageWrapperTransform"
[cdkDragFreeDragPosition]="position"
(mousedown)="onDragStarted()"
(cdkDragReleased)="onDragReleased()"
>
<ng-container *ngFor="let image of images; index as imageIndex">
<img
cdkDragHandle
class="ant-image-preview-img"
#imgRef
*ngIf="index === imageIndex"
[attr.src]="image.src"
[attr.alt]="image.alt"
[style.width]="image.width"
[style.height]="image.height"
[style.transform]="previewImageTransform"
/>
</ng-container>
</div>
<ng-container *ngIf="images.length > 1">
<div
class="ant-image-preview-switch-left"
[class.ant-image-preview-switch-left-disabled]="index <= 0"
(click)="onSwitchLeft($event)"
>
<span nz-icon nzType="left" nzTheme="outline"></span>
</div>
<div
class="ant-image-preview-switch-right"
[class.ant-image-preview-switch-right-disabled]="index >= images.length - 1"
(click)="onSwitchRight($event)"
>
<span nz-icon nzType="right" nzTheme="outline"></span>
</div>
</ng-container>
</div>
</div>
<div tabindex="0" aria-hidden="true" style="width: 0; height: 0; overflow: hidden; outline: none;"></div>
</div>
`,
preserveWhitespaces: false,
changeDetection: ChangeDetectionStrategy.OnPush,
encapsulation: ViewEncapsulation.None,
host: {
'[class.ant-image-preview-moving]': 'isDragging',
'[style.zIndex]': 'config.nzZIndex',
'[class.ant-image-preview-wrap]': 'true',
'[@.disabled]': 'config.nzNoAnimation',
'[@fadeMotion]': 'animationState',
'(@fadeMotion.start)': 'onAnimationStart($event)',
'(@fadeMotion.done)': 'onAnimationDone($event)',
'(click)': 'onContainerClick($event)',
tabindex: '-1',
role: 'document'
}
},] }
];
NzImagePreviewComponent.ctorParameters = () => [
{ type: ChangeDetectorRef },
{ type: NzConfigService },
{ type: NzImagePreviewOptions },
{ type: OverlayRef }
];
NzImagePreviewComponent.propDecorators = {
imageRef: [{ type: ViewChild, args: ['imgRef',] }]
};
/**
* 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 NzImageService {
constructor(overlay, injector, nzConfigService, directionality) {
this.overlay = overlay;
this.injector = injector;
this.nzConfigService = nzConfigService;
this.directionality = directionality;
}
preview(images, options) {
return this.display(images, options);
}
display(images, config) {
const configMerged = Object.assign(Object.assign({}, new NzImagePreviewOptions()), (config !== null && config !== void 0 ? config : {}));
const overlayRef = this.createOverlay(configMerged);
const previewComponent = this.attachPreviewComponent(overlayRef, configMerged);
previewComponent.setImages(images);
const previewRef = new NzImagePreviewRef(previewComponent, configMerged, overlayRef);
previewComponent.previewRef = previewRef;
return previewRef;
}
attachPreviewComponent(overlayRef, config) {
const injector = Injector.create({
parent: this.injector,
providers: [
{ provide: OverlayRef, useValue: overlayRef },
{ provide: NzImagePreviewOptions, useValue: config }
]
});
const containerPortal = new ComponentPortal(NzImagePreviewComponent, null, injector);
const containerRef = overlayRef.attach(containerPortal);
return containerRef.instance;
}
createOverlay(config) {
var _a, _b;
const globalConfig = this.nzConfigService.getConfigForComponent(NZ_CONFIG_MODULE_NAME) || {};
const overLayConfig = new OverlayConfig({
hasBackdrop: true,
scrollStrategy: this.overlay.scrollStrategies.block(),
positionStrategy: this.overlay.position().global(),
disposeOnNavigation: (_b = (_a = config.nzCloseOnNavigation) !== null && _a !== void 0 ? _a : globalConfig.nzCloseOnNavigation) !== null && _b !== void 0 ? _b : true,
backdropClass: IMAGE_PREVIEW_MASK_CLASS_NAME,
direction: config.nzDirection || globalConfig.nzDirection || this.directionality.value
});
return this.overlay.create(overLayConfig);
}
}
NzImageService.decorators = [
{ type: Injectable }
];
NzImageService.ctorParameters = () => [
{ type: Overlay },
{ type: Injector },
{ type: NzConfigService },
{ type: Directionality, decorators: [{ type: Optional }] }
];
const NZ_CONFIG_MODULE_NAME$1 = 'image';
class NzImageDirective {
constructor(nzConfigService, elementRef, nzImageService, cdr, parentGroup, directionality) {
this.nzConfigService = nzConfigService;
this.elementRef = elementRef;
this.nzImageService = nzImageService;
this.cdr = cdr;
this.parentGroup = parentGroup;
this.directionality = directionality;
this._nzModuleName = NZ_CONFIG_MODULE_NAME$1;
this.nzSrc = '';
this.nzDisablePreview = false;
this.nzFallback = null;
this.nzPlaceholder = null;
this.status = 'normal';
this.destroy$ = new Subject();
}
get previewable() {
return !this.nzDisablePreview && this.status !== 'error';
}
ngOnInit() {
var _a;
this.backLoad();
if (this.parentGroup) {
this.parentGroup.addImage(this);
}
if (this.directionality) {
(_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.destroy$.next();
this.destroy$.complete();
}
onPreview() {
if (!this.previewable) {
return;
}
if (this.parentGroup) {
// preview inside image group
const previewAbleImages = this.parentGroup.images.filter(e => e.previewable);
const previewImages = previewAbleImages.map(e => ({ src: e.nzSrc }));
const previewIndex = previewAbleImages.findIndex(el => this === el);
const previewRef = this.nzImageService.preview(previewImages, { nzDirection: this.dir });
previewRef.switchTo(previewIndex);
}
else {
// preview not inside image group
const previewImages = [{ src: this.nzSrc }];
this.nzImageService.preview(previewImages, { nzDirection: this.dir });
}
}
getElement() {
return this.elementRef;
}
ngOnChanges(changes) {
const { nzSrc } = changes;
if (nzSrc) {
this.getElement().nativeElement.src = nzSrc.currentValue;
this.backLoad();
}
}
/**
* use internal Image object handle fallback & placeholder
* @private
*/
backLoad() {
this.backLoadImage = new Image();
this.backLoadImage.src = this.nzSrc;
this.status = 'loading';
if (this.backLoadImage.complete) {
this.status = 'normal';
this.getElement().nativeElement.src = this.nzSrc;
}
else {
if (this.nzPlaceholder) {
this.getElement().nativeElement.src = this.nzPlaceholder;
}
else {
this.getElement().nativeElement.src = this.nzSrc;
}
this.backLoadImage.onload = () => {
this.status = 'normal';
this.getElement().nativeElement.src = this.nzSrc;
};
this.backLoadImage.onerror = () => {
this.status = 'error';
if (this.nzFallback) {
this.getElement().nativeElement.src = this.nzFallback;
}
};
}
}
}
NzImageDirective.decorators = [
{ type: Directive, args: [{
selector: 'img[nz-image]',
exportAs: 'nzImage',
host: {
'(click)': 'onPreview()'
}
},] }
];
NzImageDirective.ctorParameters = () => [
{ type: NzConfigService },
{ type: ElementRef },
{ type: NzImageService },
{ type: ChangeDetectorRef },
{ type: NzImageGroupComponent, decorators: [{ type: Optional }] },
{ type: Directionality, decorators: [{ type: Optional }] }
];
NzImageDirective.propDecorators = {
nzSrc: [{ type: Input }],
nzDisablePreview: [{ type: Input }],
nzFallback: [{ type: Input }],
nzPlaceholder: [{ type: Input }]
};
__decorate([
InputBoolean(),
WithConfig(),
__metadata("design:type", Boolean)
], NzImageDirective.prototype, "nzDisablePreview", void 0);
__decorate([
WithConfig(),
__metadata("design:type", Object)
], NzImageDirective.prototype, "nzFallback", void 0);
__decorate([
WithConfig(),
__metadata("design:type", Object)
], NzImageDirective.prototype, "nzPlaceholder", void 0);
/**
* 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 NzImageModule {
}
NzImageModule.decorators = [
{ type: NgModule, args: [{
imports: [BidiModule, OverlayModule, PortalModule, DragDropModule, CommonModule, NzIconModule, NzPipesModule],
exports: [NzImageDirective, NzImagePreviewComponent, NzImageGroupComponent],
providers: [NzImageService],
entryComponents: [NzImagePreviewComponent],
declarations: [NzImageDirective, NzImagePreviewComponent, NzImageGroupComponent]
},] }
];
/**
* 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 { FADE_CLASS_NAME_MAP, IMAGE_PREVIEW_MASK_CLASS_NAME, NZ_CONFIG_MODULE_NAME, NzImageDirective, NzImageGroupComponent, NzImageModule, NzImagePreviewComponent, NzImagePreviewOptions, NzImagePreviewRef, NzImageService, getClientSize, getFitContentPosition, getOffset };
//# sourceMappingURL=ng-zorro-antd-image.js.map