ng-zorro-antd-image.mjs 43 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008
  1. import { __esDecorate, __runInitializers } from 'tslib';
  2. import { DOCUMENT } from '@angular/common';
  3. import * as i0 from '@angular/core';
  4. import { Input, ViewEncapsulation, ChangeDetectionStrategy, Component, EventEmitter, inject, ViewChild, Injector, Injectable, booleanAttribute, Directive, NgModule } from '@angular/core';
  5. import { Subject, fromEvent } from 'rxjs';
  6. import { takeUntil, filter, take, switchMap } from 'rxjs/operators';
  7. import * as i1 from 'ng-zorro-antd/core/config';
  8. import { WithConfig } from 'ng-zorro-antd/core/config';
  9. import { CdkDragHandle, CdkDrag } from '@angular/cdk/drag-drop';
  10. import { ESCAPE, LEFT_ARROW, RIGHT_ARROW, hasModifierKey } from '@angular/cdk/keycodes';
  11. import { fadeMotion } from 'ng-zorro-antd/core/animation';
  12. import * as i3 from 'ng-zorro-antd/core/services';
  13. import { NzDestroyService } from 'ng-zorro-antd/core/services';
  14. import { fromEventOutsideAngular, isNotNil } from 'ng-zorro-antd/core/util';
  15. import * as i5 from 'ng-zorro-antd/icon';
  16. import { NzIconModule } from 'ng-zorro-antd/icon';
  17. import * as i4 from '@angular/platform-browser';
  18. import * as i1$1 from '@angular/cdk/overlay';
  19. import { OverlayRef, OverlayConfig } from '@angular/cdk/overlay';
  20. import { ComponentPortal } from '@angular/cdk/portal';
  21. import * as i3$1 from '@angular/cdk/bidi';
  22. /**
  23. * Use of this source code is governed by an MIT-style license that can be
  24. * found in the LICENSE file at https://github.com/NG-ZORRO/ng-zorro-antd/blob/master/LICENSE
  25. */
  26. class NzImageGroupComponent {
  27. nzScaleStep = null;
  28. images = [];
  29. addImage(image) {
  30. this.images.push(image);
  31. }
  32. static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.2", ngImport: i0, type: NzImageGroupComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
  33. static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.2", type: NzImageGroupComponent, isStandalone: true, selector: "nz-image-group", inputs: { nzScaleStep: "nzScaleStep" }, exportAs: ["nzImageGroup"], ngImport: i0, template: '<ng-content></ng-content>', isInline: true, changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
  34. }
  35. i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.2", ngImport: i0, type: NzImageGroupComponent, decorators: [{
  36. type: Component,
  37. args: [{
  38. selector: 'nz-image-group',
  39. exportAs: 'nzImageGroup',
  40. template: '<ng-content></ng-content>',
  41. preserveWhitespaces: false,
  42. changeDetection: ChangeDetectionStrategy.OnPush,
  43. encapsulation: ViewEncapsulation.None
  44. }]
  45. }], propDecorators: { nzScaleStep: [{
  46. type: Input
  47. }] } });
  48. /**
  49. * Use of this source code is governed by an MIT-style license that can be
  50. * found in the LICENSE file at https://github.com/NG-ZORRO/ng-zorro-antd/blob/master/LICENSE
  51. */
  52. const NZ_CONFIG_MODULE_NAME$1 = 'image';
  53. /**
  54. * Use of this source code is governed by an MIT-style license that can be
  55. * found in the LICENSE file at https://github.com/NG-ZORRO/ng-zorro-antd/blob/master/LICENSE
  56. */
  57. /**
  58. * fit content details: https://github.com/NG-ZORRO/ng-zorro-antd/pull/6154#issuecomment-745025554
  59. *
  60. * calc position x,y point
  61. *
  62. * CASE (width <= clientWidth && height <= clientHeight):
  63. *
  64. * ------------- clientWidth -------------
  65. * | |
  66. * | ------ width ------ |
  67. * | | | |
  68. * | | | |
  69. * client height | |
  70. * Height | | |
  71. * | | | |
  72. * | ------------------- |
  73. * | |
  74. * | |
  75. * ---------------------------------------
  76. * fixedPosition = { x: 0, y: 0 }
  77. *
  78. *
  79. *
  80. * CASE (width > clientWidth || height > clientHeight):
  81. *
  82. * ------------- clientWidth -------------
  83. * | | |
  84. * | top |
  85. * | | |
  86. * |--left--|--------------- width -----------------
  87. * | | |
  88. * client | |
  89. * Height | |
  90. * | | |
  91. * | | |
  92. * | height |
  93. * | | |
  94. * ---------| |
  95. * | |
  96. * | |
  97. * | |
  98. * ----------------------------------------
  99. *
  100. *
  101. * - left || top > 0
  102. * left -> 0 || top -> 0
  103. *
  104. * - (left + width) < clientWidth || (top + height) < clientHeight
  105. * - left | top + width | height < clientWidth | clientHeight -> Back left | top + width | height === clientWidth | clientHeight
  106. *
  107. * DEFAULT:
  108. * - hold position
  109. *
  110. */
  111. function getFitContentPosition(params) {
  112. let fixPos = {};
  113. if (params.width <= params.clientWidth && params.height <= params.clientHeight) {
  114. fixPos = {
  115. x: 0,
  116. y: 0
  117. };
  118. }
  119. if (params.width > params.clientWidth || params.height > params.clientHeight) {
  120. fixPos = {
  121. x: fitPoint(params.left, params.width, params.clientWidth),
  122. y: fitPoint(params.top, params.height, params.clientHeight)
  123. };
  124. }
  125. return fixPos;
  126. }
  127. function getOffset(node) {
  128. const box = node.getBoundingClientRect();
  129. const docElem = document.documentElement;
  130. // use docElem.scrollLeft to support IE
  131. return {
  132. left: box.left + (window.pageXOffset || docElem.scrollLeft) - (docElem.clientLeft || document.body.clientLeft || 0),
  133. top: box.top + (window.pageYOffset || docElem.scrollTop) - (docElem.clientTop || document.body.clientTop || 0)
  134. };
  135. }
  136. function getClientSize() {
  137. const width = document.documentElement.clientWidth;
  138. const height = window.innerHeight || document.documentElement.clientHeight;
  139. return {
  140. width,
  141. height
  142. };
  143. }
  144. function fitPoint(start, size, clientSize) {
  145. const startAddSize = start + size;
  146. const offsetStart = (size - clientSize) / 2;
  147. let distance = null;
  148. if (size > clientSize) {
  149. if (start > 0) {
  150. distance = offsetStart;
  151. }
  152. if (start < 0 && startAddSize < clientSize) {
  153. distance = -offsetStart;
  154. }
  155. }
  156. else {
  157. if (start < 0 || startAddSize > clientSize) {
  158. distance = start < 0 ? offsetStart : -offsetStart;
  159. }
  160. }
  161. return distance;
  162. }
  163. /**
  164. * Use of this source code is governed by an MIT-style license that can be
  165. * found in the LICENSE file at https://github.com/NG-ZORRO/ng-zorro-antd/blob/master/LICENSE
  166. */
  167. class NzImagePreviewOptions {
  168. nzKeyboard = true;
  169. nzNoAnimation = false;
  170. nzMaskClosable = true;
  171. nzCloseOnNavigation = true;
  172. nzZIndex;
  173. nzZoom;
  174. nzRotate;
  175. nzFlipHorizontally;
  176. nzFlipVertically;
  177. nzScaleStep;
  178. nzDirection;
  179. }
  180. const initialPosition = {
  181. x: 0,
  182. y: 0
  183. };
  184. const NZ_DEFAULT_SCALE_STEP = 0.5;
  185. const NZ_DEFAULT_ZOOM = 1;
  186. const NZ_DEFAULT_ROTATE = 0;
  187. class NzImagePreviewComponent {
  188. ngZone;
  189. cdr;
  190. nzConfigService;
  191. config;
  192. destroy$;
  193. sanitizer;
  194. _defaultNzZoom = NZ_DEFAULT_ZOOM;
  195. _defaultNzScaleStep = NZ_DEFAULT_SCALE_STEP;
  196. _defaultNzRotate = NZ_DEFAULT_ROTATE;
  197. images = [];
  198. index = 0;
  199. isDragging = false;
  200. visible = true;
  201. animationStateChanged = new EventEmitter();
  202. scaleStepMap = new Map();
  203. previewImageTransform = '';
  204. previewImageWrapperTransform = '';
  205. operations = [
  206. {
  207. icon: 'close',
  208. onClick: () => {
  209. this.onClose();
  210. },
  211. type: 'close'
  212. },
  213. {
  214. icon: 'zoom-in',
  215. onClick: () => {
  216. this.onZoomIn();
  217. },
  218. type: 'zoomIn'
  219. },
  220. {
  221. icon: 'zoom-out',
  222. onClick: () => {
  223. this.onZoomOut();
  224. },
  225. type: 'zoomOut'
  226. },
  227. {
  228. icon: 'rotate-right',
  229. onClick: () => {
  230. this.onRotateRight();
  231. },
  232. type: 'rotateRight'
  233. },
  234. {
  235. icon: 'rotate-left',
  236. onClick: () => {
  237. this.onRotateLeft();
  238. },
  239. type: 'rotateLeft'
  240. },
  241. {
  242. icon: 'swap',
  243. onClick: () => {
  244. this.onHorizontalFlip();
  245. },
  246. type: 'flipHorizontally'
  247. },
  248. {
  249. icon: 'swap',
  250. onClick: () => {
  251. this.onVerticalFlip();
  252. },
  253. type: 'flipVertically',
  254. rotate: 90
  255. }
  256. ];
  257. zoomOutDisabled = false;
  258. position = { ...initialPosition };
  259. previewRef;
  260. closeClick = new EventEmitter();
  261. imageRef;
  262. imagePreviewWrapper;
  263. document = inject(DOCUMENT);
  264. zoom;
  265. rotate;
  266. scaleStep;
  267. flipHorizontally;
  268. flipVertically;
  269. get animationDisabled() {
  270. return this.config.nzNoAnimation ?? false;
  271. }
  272. get maskClosable() {
  273. const defaultConfig = this.nzConfigService.getConfigForComponent(NZ_CONFIG_MODULE_NAME$1) || {};
  274. return this.config.nzMaskClosable ?? defaultConfig.nzMaskClosable ?? true;
  275. }
  276. constructor(ngZone, cdr, nzConfigService, config, destroy$, sanitizer) {
  277. this.ngZone = ngZone;
  278. this.cdr = cdr;
  279. this.nzConfigService = nzConfigService;
  280. this.config = config;
  281. this.destroy$ = destroy$;
  282. this.sanitizer = sanitizer;
  283. this.zoom = this.config.nzZoom ?? this._defaultNzZoom;
  284. this.scaleStep = this.config.nzScaleStep ?? this._defaultNzScaleStep;
  285. this.rotate = this.config.nzRotate ?? this._defaultNzRotate;
  286. this.flipHorizontally = this.config.nzFlipHorizontally ?? false;
  287. this.flipVertically = this.config.nzFlipVertically ?? false;
  288. this.updateZoomOutDisabled();
  289. this.updatePreviewImageTransform();
  290. this.updatePreviewImageWrapperTransform();
  291. }
  292. ngOnInit() {
  293. fromEventOutsideAngular(this.imagePreviewWrapper.nativeElement, 'mousedown')
  294. .pipe(takeUntil(this.destroy$))
  295. .subscribe(() => {
  296. this.isDragging = true;
  297. });
  298. fromEventOutsideAngular(this.imagePreviewWrapper.nativeElement, 'wheel')
  299. .pipe(takeUntil(this.destroy$))
  300. .subscribe(event => {
  301. this.ngZone.run(() => this.wheelZoomEventHandler(event));
  302. });
  303. fromEventOutsideAngular(this.document, 'keydown')
  304. .pipe(filter(event => event.keyCode === ESCAPE), takeUntil(this.destroy$))
  305. .subscribe(() => {
  306. this.ngZone.run(() => {
  307. this.onClose();
  308. this.markForCheck();
  309. });
  310. });
  311. }
  312. setImages(images, scaleStepMap) {
  313. if (scaleStepMap)
  314. this.scaleStepMap = scaleStepMap;
  315. this.images = images;
  316. this.markForCheck();
  317. }
  318. switchTo(index) {
  319. this.index = index;
  320. this.markForCheck();
  321. }
  322. next() {
  323. if (this.index < this.images.length - 1) {
  324. this.reset();
  325. this.index++;
  326. this.updatePreviewImageTransform();
  327. this.updatePreviewImageWrapperTransform();
  328. this.updateZoomOutDisabled();
  329. this.markForCheck();
  330. }
  331. }
  332. prev() {
  333. if (this.index > 0) {
  334. this.reset();
  335. this.index--;
  336. this.updatePreviewImageTransform();
  337. this.updatePreviewImageWrapperTransform();
  338. this.updateZoomOutDisabled();
  339. this.markForCheck();
  340. }
  341. }
  342. markForCheck() {
  343. this.cdr.markForCheck();
  344. }
  345. onClose() {
  346. this.visible = false;
  347. this.closeClick.emit();
  348. }
  349. onZoomIn() {
  350. const zoomStep = this.scaleStepMap.get(this.images[this.index].src ?? this.images[this.index].srcset) ?? this.scaleStep;
  351. this.zoom += zoomStep;
  352. this.updatePreviewImageTransform();
  353. this.updateZoomOutDisabled();
  354. }
  355. onZoomOut() {
  356. if (this.zoom > 1) {
  357. const zoomStep = this.scaleStepMap.get(this.images[this.index].src ?? this.images[this.index].srcset) ?? this.scaleStep;
  358. this.zoom -= zoomStep;
  359. this.updatePreviewImageTransform();
  360. this.updateZoomOutDisabled();
  361. if (this.zoom <= 1) {
  362. this.reCenterImage();
  363. }
  364. }
  365. }
  366. onRotateRight() {
  367. this.rotate += 90;
  368. this.updatePreviewImageTransform();
  369. }
  370. onRotateLeft() {
  371. this.rotate -= 90;
  372. this.updatePreviewImageTransform();
  373. }
  374. onSwitchLeft(event) {
  375. event.preventDefault();
  376. event.stopPropagation();
  377. this.prev();
  378. }
  379. onSwitchRight(event) {
  380. event.preventDefault();
  381. event.stopPropagation();
  382. this.next();
  383. }
  384. onHorizontalFlip() {
  385. this.flipHorizontally = !this.flipHorizontally;
  386. this.updatePreviewImageTransform();
  387. }
  388. onVerticalFlip() {
  389. this.flipVertically = !this.flipVertically;
  390. this.updatePreviewImageTransform();
  391. }
  392. wheelZoomEventHandler(event) {
  393. event.preventDefault();
  394. event.stopPropagation();
  395. this.handlerImageTransformationWhileZoomingWithMouse(event, event.deltaY);
  396. this.handleImageScaleWhileZoomingWithMouse(event.deltaY);
  397. this.updatePreviewImageWrapperTransform();
  398. this.updatePreviewImageTransform();
  399. this.markForCheck();
  400. }
  401. onAnimationStart(event) {
  402. this.animationStateChanged.emit(event);
  403. }
  404. onAnimationDone(event) {
  405. this.animationStateChanged.emit(event);
  406. }
  407. onDragEnd(event) {
  408. this.isDragging = false;
  409. const width = this.imageRef.nativeElement.offsetWidth * this.zoom;
  410. const height = this.imageRef.nativeElement.offsetHeight * this.zoom;
  411. const { left, top } = getOffset(this.imageRef.nativeElement);
  412. const { width: clientWidth, height: clientHeight } = getClientSize();
  413. const isRotate = this.rotate % 180 !== 0;
  414. const fitContentParams = {
  415. width: isRotate ? height : width,
  416. height: isRotate ? width : height,
  417. left,
  418. top,
  419. clientWidth,
  420. clientHeight
  421. };
  422. const fitContentPos = getFitContentPosition(fitContentParams);
  423. if (isNotNil(fitContentPos.x) || isNotNil(fitContentPos.y)) {
  424. this.position = { ...this.position, ...fitContentPos };
  425. }
  426. else if (!isNotNil(fitContentPos.x) && !isNotNil(fitContentPos.y)) {
  427. this.position = {
  428. x: event.source.getFreeDragPosition().x,
  429. y: event.source.getFreeDragPosition().y
  430. };
  431. }
  432. }
  433. sanitizerResourceUrl(url) {
  434. return this.sanitizer.bypassSecurityTrustResourceUrl(url);
  435. }
  436. updatePreviewImageTransform() {
  437. this.previewImageTransform = `scale3d(${this.zoom * (this.flipHorizontally ? -1 : 1)}, ${this.zoom * (this.flipVertically ? -1 : 1)}, 1) rotate(${this.rotate}deg)`;
  438. }
  439. updatePreviewImageWrapperTransform() {
  440. this.previewImageWrapperTransform = `translate3d(${this.position.x}px, ${this.position.y}px, 0)`;
  441. }
  442. updateZoomOutDisabled() {
  443. this.zoomOutDisabled = this.zoom <= 1;
  444. }
  445. handlerImageTransformationWhileZoomingWithMouse(event, deltaY) {
  446. let scaleValue;
  447. const imageElement = this.imageRef.nativeElement;
  448. const elementTransform = getComputedStyle(imageElement).transform;
  449. const matrixValue = elementTransform.match(/matrix.*\((.+)\)/);
  450. if (matrixValue) {
  451. scaleValue = +matrixValue[1].split(', ')[0];
  452. }
  453. else {
  454. scaleValue = this.zoom;
  455. }
  456. const x = (event.clientX - imageElement.getBoundingClientRect().x) / scaleValue;
  457. const y = (event.clientY - imageElement.getBoundingClientRect().y) / scaleValue;
  458. const halfOfScaleStepValue = deltaY < 0 ? this.scaleStep / 2 : -this.scaleStep / 2;
  459. this.position.x += -x * halfOfScaleStepValue * 2 + imageElement.offsetWidth * halfOfScaleStepValue;
  460. this.position.y += -y * halfOfScaleStepValue * 2 + imageElement.offsetHeight * halfOfScaleStepValue;
  461. }
  462. handleImageScaleWhileZoomingWithMouse(deltaY) {
  463. if (this.isZoomedInWithMouseWheel(deltaY)) {
  464. this.onZoomIn();
  465. }
  466. else {
  467. this.onZoomOut();
  468. }
  469. if (this.zoom <= 1) {
  470. this.reCenterImage();
  471. }
  472. }
  473. isZoomedInWithMouseWheel(delta) {
  474. return delta < 0;
  475. }
  476. reset() {
  477. this.zoom = this.config.nzZoom ?? this._defaultNzZoom;
  478. this.scaleStep = this.config.nzScaleStep ?? this._defaultNzScaleStep;
  479. this.rotate = this.config.nzRotate ?? this._defaultNzRotate;
  480. this.flipHorizontally = false;
  481. this.flipVertically = false;
  482. this.reCenterImage();
  483. }
  484. reCenterImage() {
  485. this.position = { ...initialPosition };
  486. }
  487. static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.2", ngImport: i0, type: NzImagePreviewComponent, deps: [{ token: i0.NgZone }, { token: i0.ChangeDetectorRef }, { token: i1.NzConfigService }, { token: NzImagePreviewOptions }, { token: i3.NzDestroyService }, { token: i4.DomSanitizer }], target: i0.ɵɵFactoryTarget.Component });
  488. static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.2", type: NzImagePreviewComponent, isStandalone: true, selector: "nz-image-preview", host: { listeners: { "@fadeMotion.start": "onAnimationStart($event)", "@fadeMotion.done": "onAnimationDone($event)" }, properties: { "class.ant-image-preview-moving": "isDragging", "style.zIndex": "config.nzZIndex", "@.disabled": "config.nzNoAnimation", "@fadeMotion": "visible ? 'enter' : 'leave'" }, classAttribute: "ant-image-preview-root" }, providers: [NzDestroyService], viewQueries: [{ propertyName: "imageRef", first: true, predicate: ["imgRef"], descendants: true }, { propertyName: "imagePreviewWrapper", first: true, predicate: ["imagePreviewWrapper"], descendants: true, static: true }], exportAs: ["nzImagePreview"], ngImport: i0, template: `
  489. <div class="ant-image-preview-mask"></div>
  490. <div class="ant-image-preview-operations-wrapper">
  491. @if (images.length > 1) {
  492. <div
  493. class="ant-image-preview-switch-left"
  494. [class.ant-image-preview-switch-left-disabled]="index <= 0"
  495. (click)="onSwitchLeft($event)"
  496. >
  497. <nz-icon nzType="left" nzTheme="outline" />
  498. </div>
  499. <div
  500. class="ant-image-preview-switch-right"
  501. [class.ant-image-preview-switch-right-disabled]="index >= images.length - 1"
  502. (click)="onSwitchRight($event)"
  503. >
  504. <nz-icon nzType="right" nzTheme="outline" />
  505. </div>
  506. }
  507. <ul class="ant-image-preview-operations">
  508. @if (images.length > 1) {
  509. <li class="ant-image-preview-operations-progress">{{ index + 1 }} / {{ images.length }}</li>
  510. }
  511. @for (option of operations; track option) {
  512. <li
  513. class="ant-image-preview-operations-operation"
  514. [class.ant-image-preview-operations-operation-disabled]="zoomOutDisabled && option.type === 'zoomOut'"
  515. (click)="option.onClick()"
  516. >
  517. <nz-icon
  518. class="ant-image-preview-operations-icon"
  519. [nzType]="option.icon"
  520. [nzRotate]="option.rotate ?? 0"
  521. nzTheme="outline"
  522. />
  523. </li>
  524. }
  525. </ul>
  526. </div>
  527. <div
  528. class="ant-image-preview-wrap"
  529. tabindex="-1"
  530. (click)="maskClosable && $event.target === $event.currentTarget && onClose()"
  531. >
  532. <div class="ant-image-preview" role="dialog" aria-modal="true">
  533. <div tabindex="0" aria-hidden="true" class="ant-image-preview-focus-trap"></div>
  534. <div class="ant-image-preview-content">
  535. <div class="ant-image-preview-body">
  536. <div
  537. class="ant-image-preview-img-wrapper"
  538. #imagePreviewWrapper
  539. cdkDrag
  540. [style.transform]="previewImageWrapperTransform"
  541. [cdkDragFreeDragPosition]="position"
  542. (cdkDragEnded)="onDragEnd($event)"
  543. >
  544. @for (image of images; track image; let imageIndex = $index) {
  545. @if (imageIndex === index) {
  546. <img
  547. cdkDragHandle
  548. class="ant-image-preview-img"
  549. #imgRef
  550. [attr.src]="sanitizerResourceUrl(image.src)"
  551. [attr.srcset]="image.srcset"
  552. [attr.alt]="image.alt"
  553. [style.width]="image.width"
  554. [style.height]="image.height"
  555. [style.transform]="previewImageTransform"
  556. />
  557. }
  558. }
  559. </div>
  560. </div>
  561. </div>
  562. <div tabindex="0" aria-hidden="true" class="ant-image-preview-focus-trap"></div>
  563. </div>
  564. </div>
  565. `, isInline: true, dependencies: [{ kind: "ngmodule", type: NzIconModule }, { kind: "directive", type: i5.NzIconDirective, selector: "nz-icon,[nz-icon]", inputs: ["nzSpin", "nzRotate", "nzType", "nzTheme", "nzTwotoneColor", "nzIconfont"], exportAs: ["nzIcon"] }, { kind: "directive", type: CdkDragHandle, selector: "[cdkDragHandle]", inputs: ["cdkDragHandleDisabled"] }, { kind: "directive", type: CdkDrag, selector: "[cdkDrag]", inputs: ["cdkDragData", "cdkDragLockAxis", "cdkDragRootElement", "cdkDragBoundary", "cdkDragStartDelay", "cdkDragFreeDragPosition", "cdkDragDisabled", "cdkDragConstrainPosition", "cdkDragPreviewClass", "cdkDragPreviewContainer", "cdkDragScale"], outputs: ["cdkDragStarted", "cdkDragReleased", "cdkDragEnded", "cdkDragEntered", "cdkDragExited", "cdkDragDropped", "cdkDragMoved"], exportAs: ["cdkDrag"] }], animations: [fadeMotion], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
  566. }
  567. i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.2", ngImport: i0, type: NzImagePreviewComponent, decorators: [{
  568. type: Component,
  569. args: [{
  570. selector: 'nz-image-preview',
  571. exportAs: 'nzImagePreview',
  572. animations: [fadeMotion],
  573. template: `
  574. <div class="ant-image-preview-mask"></div>
  575. <div class="ant-image-preview-operations-wrapper">
  576. @if (images.length > 1) {
  577. <div
  578. class="ant-image-preview-switch-left"
  579. [class.ant-image-preview-switch-left-disabled]="index <= 0"
  580. (click)="onSwitchLeft($event)"
  581. >
  582. <nz-icon nzType="left" nzTheme="outline" />
  583. </div>
  584. <div
  585. class="ant-image-preview-switch-right"
  586. [class.ant-image-preview-switch-right-disabled]="index >= images.length - 1"
  587. (click)="onSwitchRight($event)"
  588. >
  589. <nz-icon nzType="right" nzTheme="outline" />
  590. </div>
  591. }
  592. <ul class="ant-image-preview-operations">
  593. @if (images.length > 1) {
  594. <li class="ant-image-preview-operations-progress">{{ index + 1 }} / {{ images.length }}</li>
  595. }
  596. @for (option of operations; track option) {
  597. <li
  598. class="ant-image-preview-operations-operation"
  599. [class.ant-image-preview-operations-operation-disabled]="zoomOutDisabled && option.type === 'zoomOut'"
  600. (click)="option.onClick()"
  601. >
  602. <nz-icon
  603. class="ant-image-preview-operations-icon"
  604. [nzType]="option.icon"
  605. [nzRotate]="option.rotate ?? 0"
  606. nzTheme="outline"
  607. />
  608. </li>
  609. }
  610. </ul>
  611. </div>
  612. <div
  613. class="ant-image-preview-wrap"
  614. tabindex="-1"
  615. (click)="maskClosable && $event.target === $event.currentTarget && onClose()"
  616. >
  617. <div class="ant-image-preview" role="dialog" aria-modal="true">
  618. <div tabindex="0" aria-hidden="true" class="ant-image-preview-focus-trap"></div>
  619. <div class="ant-image-preview-content">
  620. <div class="ant-image-preview-body">
  621. <div
  622. class="ant-image-preview-img-wrapper"
  623. #imagePreviewWrapper
  624. cdkDrag
  625. [style.transform]="previewImageWrapperTransform"
  626. [cdkDragFreeDragPosition]="position"
  627. (cdkDragEnded)="onDragEnd($event)"
  628. >
  629. @for (image of images; track image; let imageIndex = $index) {
  630. @if (imageIndex === index) {
  631. <img
  632. cdkDragHandle
  633. class="ant-image-preview-img"
  634. #imgRef
  635. [attr.src]="sanitizerResourceUrl(image.src)"
  636. [attr.srcset]="image.srcset"
  637. [attr.alt]="image.alt"
  638. [style.width]="image.width"
  639. [style.height]="image.height"
  640. [style.transform]="previewImageTransform"
  641. />
  642. }
  643. }
  644. </div>
  645. </div>
  646. </div>
  647. <div tabindex="0" aria-hidden="true" class="ant-image-preview-focus-trap"></div>
  648. </div>
  649. </div>
  650. `,
  651. preserveWhitespaces: false,
  652. changeDetection: ChangeDetectionStrategy.OnPush,
  653. encapsulation: ViewEncapsulation.None,
  654. host: {
  655. class: 'ant-image-preview-root',
  656. '[class.ant-image-preview-moving]': 'isDragging',
  657. '[style.zIndex]': 'config.nzZIndex',
  658. '[@.disabled]': 'config.nzNoAnimation',
  659. '[@fadeMotion]': `visible ? 'enter' : 'leave'`,
  660. '(@fadeMotion.start)': 'onAnimationStart($event)',
  661. '(@fadeMotion.done)': 'onAnimationDone($event)'
  662. },
  663. imports: [NzIconModule, CdkDragHandle, CdkDrag],
  664. providers: [NzDestroyService]
  665. }]
  666. }], ctorParameters: () => [{ type: i0.NgZone }, { type: i0.ChangeDetectorRef }, { type: i1.NzConfigService }, { type: NzImagePreviewOptions }, { type: i3.NzDestroyService }, { type: i4.DomSanitizer }], propDecorators: { imageRef: [{
  667. type: ViewChild,
  668. args: ['imgRef']
  669. }], imagePreviewWrapper: [{
  670. type: ViewChild,
  671. args: ['imagePreviewWrapper', { static: true }]
  672. }] } });
  673. /**
  674. * Use of this source code is governed by an MIT-style license that can be
  675. * found in the LICENSE file at https://github.com/NG-ZORRO/ng-zorro-antd/blob/master/LICENSE
  676. */
  677. class NzImagePreviewRef {
  678. previewInstance;
  679. config;
  680. overlayRef;
  681. destroy$ = new Subject();
  682. constructor(previewInstance, config, overlayRef) {
  683. this.previewInstance = previewInstance;
  684. this.config = config;
  685. this.overlayRef = overlayRef;
  686. overlayRef
  687. .keydownEvents()
  688. .pipe(filter(event => this.config.nzKeyboard &&
  689. (event.keyCode === ESCAPE || event.keyCode === LEFT_ARROW || event.keyCode === RIGHT_ARROW) &&
  690. !hasModifierKey(event)))
  691. .subscribe(event => {
  692. event.preventDefault();
  693. if (event.keyCode === ESCAPE) {
  694. previewInstance.onClose();
  695. }
  696. if (event.keyCode === LEFT_ARROW) {
  697. this.prev();
  698. }
  699. if (event.keyCode === RIGHT_ARROW) {
  700. this.next();
  701. }
  702. });
  703. overlayRef.detachments().subscribe(() => {
  704. this.overlayRef.dispose();
  705. });
  706. previewInstance.closeClick
  707. .pipe(take(1), switchMap(() => previewInstance.animationStateChanged), filter(event => event.phaseName === 'done'), takeUntil(this.destroy$))
  708. .subscribe(() => {
  709. this.close();
  710. });
  711. }
  712. switchTo(index) {
  713. this.previewInstance.switchTo(index);
  714. }
  715. next() {
  716. this.previewInstance.next();
  717. }
  718. prev() {
  719. this.previewInstance.prev();
  720. }
  721. close() {
  722. this.destroy$.next();
  723. this.overlayRef.dispose();
  724. }
  725. }
  726. // eslint-disable-next-line @typescript-eslint/no-unsafe-declaration-merging
  727. class NzImageService {
  728. overlay;
  729. injector;
  730. nzConfigService;
  731. directionality;
  732. constructor(overlay, injector, nzConfigService, directionality) {
  733. this.overlay = overlay;
  734. this.injector = injector;
  735. this.nzConfigService = nzConfigService;
  736. this.directionality = directionality;
  737. }
  738. preview(images, options, zoomMap) {
  739. return this.display(images, options, zoomMap);
  740. }
  741. display(images, config, scaleStepMap) {
  742. const configMerged = { ...new NzImagePreviewOptions(), ...(config ?? {}) };
  743. const overlayRef = this.createOverlay(configMerged);
  744. const previewComponent = this.attachPreviewComponent(overlayRef, configMerged);
  745. previewComponent.setImages(images, scaleStepMap);
  746. const previewRef = new NzImagePreviewRef(previewComponent, configMerged, overlayRef);
  747. previewComponent.previewRef = previewRef;
  748. return previewRef;
  749. }
  750. attachPreviewComponent(overlayRef, config) {
  751. const injector = Injector.create({
  752. parent: this.injector,
  753. providers: [
  754. { provide: OverlayRef, useValue: overlayRef },
  755. { provide: NzImagePreviewOptions, useValue: config }
  756. ]
  757. });
  758. const containerPortal = new ComponentPortal(NzImagePreviewComponent, null, injector);
  759. const containerRef = overlayRef.attach(containerPortal);
  760. return containerRef.instance;
  761. }
  762. createOverlay(config) {
  763. const globalConfig = this.nzConfigService.getConfigForComponent(NZ_CONFIG_MODULE_NAME$1) || {};
  764. const overLayConfig = new OverlayConfig({
  765. scrollStrategy: this.overlay.scrollStrategies.block(),
  766. positionStrategy: this.overlay.position().global(),
  767. disposeOnNavigation: config.nzCloseOnNavigation ?? globalConfig.nzCloseOnNavigation ?? true,
  768. direction: config.nzDirection || globalConfig.nzDirection || this.directionality.value
  769. });
  770. return this.overlay.create(overLayConfig);
  771. }
  772. static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.2", ngImport: i0, type: NzImageService, deps: [{ token: i1$1.Overlay }, { token: i0.Injector }, { token: i1.NzConfigService }, { token: i3$1.Directionality }], target: i0.ɵɵFactoryTarget.Injectable });
  773. static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.2", ngImport: i0, type: NzImageService });
  774. }
  775. i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.2", ngImport: i0, type: NzImageService, decorators: [{
  776. type: Injectable
  777. }], ctorParameters: () => [{ type: i1$1.Overlay }, { type: i0.Injector }, { type: i1.NzConfigService }, { type: i3$1.Directionality }] });
  778. const NZ_CONFIG_MODULE_NAME = 'image';
  779. let NzImageDirective = (() => {
  780. let _nzDisablePreview_decorators;
  781. let _nzDisablePreview_initializers = [];
  782. let _nzDisablePreview_extraInitializers = [];
  783. let _nzFallback_decorators;
  784. let _nzFallback_initializers = [];
  785. let _nzFallback_extraInitializers = [];
  786. let _nzPlaceholder_decorators;
  787. let _nzPlaceholder_initializers = [];
  788. let _nzPlaceholder_extraInitializers = [];
  789. let _nzScaleStep_decorators;
  790. let _nzScaleStep_initializers = [];
  791. let _nzScaleStep_extraInitializers = [];
  792. return class NzImageDirective {
  793. static {
  794. const _metadata = typeof Symbol === "function" && Symbol.metadata ? Object.create(null) : void 0;
  795. _nzDisablePreview_decorators = [WithConfig()];
  796. _nzFallback_decorators = [WithConfig()];
  797. _nzPlaceholder_decorators = [WithConfig()];
  798. _nzScaleStep_decorators = [WithConfig()];
  799. __esDecorate(null, null, _nzDisablePreview_decorators, { kind: "field", name: "nzDisablePreview", static: false, private: false, access: { has: obj => "nzDisablePreview" in obj, get: obj => obj.nzDisablePreview, set: (obj, value) => { obj.nzDisablePreview = value; } }, metadata: _metadata }, _nzDisablePreview_initializers, _nzDisablePreview_extraInitializers);
  800. __esDecorate(null, null, _nzFallback_decorators, { kind: "field", name: "nzFallback", static: false, private: false, access: { has: obj => "nzFallback" in obj, get: obj => obj.nzFallback, set: (obj, value) => { obj.nzFallback = value; } }, metadata: _metadata }, _nzFallback_initializers, _nzFallback_extraInitializers);
  801. __esDecorate(null, null, _nzPlaceholder_decorators, { kind: "field", name: "nzPlaceholder", static: false, private: false, access: { has: obj => "nzPlaceholder" in obj, get: obj => obj.nzPlaceholder, set: (obj, value) => { obj.nzPlaceholder = value; } }, metadata: _metadata }, _nzPlaceholder_initializers, _nzPlaceholder_extraInitializers);
  802. __esDecorate(null, null, _nzScaleStep_decorators, { kind: "field", name: "nzScaleStep", static: false, private: false, access: { has: obj => "nzScaleStep" in obj, get: obj => obj.nzScaleStep, set: (obj, value) => { obj.nzScaleStep = value; } }, metadata: _metadata }, _nzScaleStep_initializers, _nzScaleStep_extraInitializers);
  803. if (_metadata) Object.defineProperty(this, Symbol.metadata, { enumerable: true, configurable: true, writable: true, value: _metadata });
  804. }
  805. nzConfigService;
  806. elementRef;
  807. nzImageService;
  808. cdr;
  809. directionality;
  810. _nzModuleName = NZ_CONFIG_MODULE_NAME;
  811. nzSrc = '';
  812. nzSrcset = '';
  813. nzDisablePreview = __runInitializers(this, _nzDisablePreview_initializers, false);
  814. nzFallback = (__runInitializers(this, _nzDisablePreview_extraInitializers), __runInitializers(this, _nzFallback_initializers, null));
  815. nzPlaceholder = (__runInitializers(this, _nzFallback_extraInitializers), __runInitializers(this, _nzPlaceholder_initializers, null));
  816. nzScaleStep = (__runInitializers(this, _nzPlaceholder_extraInitializers), __runInitializers(this, _nzScaleStep_initializers, null));
  817. dir = __runInitializers(this, _nzScaleStep_extraInitializers);
  818. backLoadImage;
  819. status = 'normal';
  820. backLoadDestroy$ = new Subject();
  821. destroy$ = new Subject();
  822. document = inject(DOCUMENT);
  823. parentGroup = inject(NzImageGroupComponent, { optional: true });
  824. get previewable() {
  825. return !this.nzDisablePreview && this.status !== 'error';
  826. }
  827. constructor(nzConfigService, elementRef, nzImageService, cdr, directionality) {
  828. this.nzConfigService = nzConfigService;
  829. this.elementRef = elementRef;
  830. this.nzImageService = nzImageService;
  831. this.cdr = cdr;
  832. this.directionality = directionality;
  833. }
  834. ngOnInit() {
  835. this.backLoad();
  836. if (this.parentGroup) {
  837. this.parentGroup.addImage(this);
  838. }
  839. if (this.directionality) {
  840. this.directionality.change?.pipe(takeUntil(this.destroy$)).subscribe((direction) => {
  841. this.dir = direction;
  842. this.cdr.detectChanges();
  843. });
  844. this.dir = this.directionality.value;
  845. }
  846. }
  847. ngOnDestroy() {
  848. this.destroy$.next();
  849. this.destroy$.complete();
  850. }
  851. onPreview() {
  852. if (!this.previewable) {
  853. return;
  854. }
  855. if (this.parentGroup) {
  856. // preview inside image group
  857. const previewAbleImages = this.parentGroup.images.filter(e => e.previewable);
  858. const previewImages = previewAbleImages.map(e => ({ src: e.nzSrc, srcset: e.nzSrcset }));
  859. const previewIndex = previewAbleImages.findIndex(el => this === el);
  860. const scaleStepMap = new Map();
  861. previewAbleImages.forEach(imageDirective => {
  862. scaleStepMap.set(imageDirective.nzSrc ?? imageDirective.nzSrcset, imageDirective.nzScaleStep ?? this.parentGroup.nzScaleStep ?? this.nzScaleStep ?? NZ_DEFAULT_SCALE_STEP);
  863. });
  864. const previewRef = this.nzImageService.preview(previewImages, {
  865. nzDirection: this.dir
  866. }, scaleStepMap);
  867. previewRef.switchTo(previewIndex);
  868. }
  869. else {
  870. // preview not inside image group
  871. const previewImages = [{ src: this.nzSrc, srcset: this.nzSrcset }];
  872. this.nzImageService.preview(previewImages, {
  873. nzDirection: this.dir,
  874. nzScaleStep: this.nzScaleStep ?? NZ_DEFAULT_SCALE_STEP
  875. });
  876. }
  877. }
  878. getElement() {
  879. return this.elementRef;
  880. }
  881. ngOnChanges(changes) {
  882. const { nzSrc } = changes;
  883. if (nzSrc) {
  884. this.getElement().nativeElement.src = nzSrc.currentValue;
  885. this.backLoad();
  886. }
  887. }
  888. /**
  889. * use internal Image object handle fallback & placeholder
  890. *
  891. * @private
  892. */
  893. backLoad() {
  894. this.backLoadImage = this.document.createElement('img');
  895. this.backLoadImage.src = this.nzSrc;
  896. this.backLoadImage.srcset = this.nzSrcset;
  897. this.status = 'loading';
  898. // unsubscribe last backLoad
  899. this.backLoadDestroy$.next();
  900. this.backLoadDestroy$.complete();
  901. this.backLoadDestroy$ = new Subject();
  902. if (this.backLoadImage.complete) {
  903. this.status = 'normal';
  904. this.getElement().nativeElement.src = this.nzSrc;
  905. this.getElement().nativeElement.srcset = this.nzSrcset;
  906. }
  907. else {
  908. if (this.nzPlaceholder) {
  909. this.getElement().nativeElement.src = this.nzPlaceholder;
  910. this.getElement().nativeElement.srcset = '';
  911. }
  912. else {
  913. this.getElement().nativeElement.src = this.nzSrc;
  914. this.getElement().nativeElement.srcset = this.nzSrcset;
  915. }
  916. // The `nz-image` directive can be destroyed before the `load` or `error` event is dispatched,
  917. // so there's no sense to keep capturing `this`.
  918. fromEvent(this.backLoadImage, 'load')
  919. .pipe(takeUntil(this.backLoadDestroy$), takeUntil(this.destroy$))
  920. .subscribe(() => {
  921. this.status = 'normal';
  922. this.getElement().nativeElement.src = this.nzSrc;
  923. this.getElement().nativeElement.srcset = this.nzSrcset;
  924. });
  925. fromEvent(this.backLoadImage, 'error')
  926. .pipe(takeUntil(this.backLoadDestroy$), takeUntil(this.destroy$))
  927. .subscribe(() => {
  928. this.status = 'error';
  929. if (this.nzFallback) {
  930. this.getElement().nativeElement.src = this.nzFallback;
  931. this.getElement().nativeElement.srcset = '';
  932. }
  933. });
  934. }
  935. }
  936. static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.2", ngImport: i0, type: NzImageDirective, deps: [{ token: i1.NzConfigService }, { token: i0.ElementRef }, { token: NzImageService }, { token: i0.ChangeDetectorRef }, { token: i3$1.Directionality }], target: i0.ɵɵFactoryTarget.Directive });
  937. static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "16.1.0", version: "19.2.2", type: NzImageDirective, isStandalone: true, selector: "img[nz-image]", inputs: { nzSrc: "nzSrc", nzSrcset: "nzSrcset", nzDisablePreview: ["nzDisablePreview", "nzDisablePreview", booleanAttribute], nzFallback: "nzFallback", nzPlaceholder: "nzPlaceholder", nzScaleStep: "nzScaleStep" }, host: { listeners: { "click": "onPreview()" } }, exportAs: ["nzImage"], usesOnChanges: true, ngImport: i0 });
  938. };
  939. })();
  940. i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.2", ngImport: i0, type: NzImageDirective, decorators: [{
  941. type: Directive,
  942. args: [{
  943. selector: 'img[nz-image]',
  944. exportAs: 'nzImage',
  945. host: {
  946. '(click)': 'onPreview()'
  947. }
  948. }]
  949. }], ctorParameters: () => [{ type: i1.NzConfigService }, { type: i0.ElementRef }, { type: NzImageService }, { type: i0.ChangeDetectorRef }, { type: i3$1.Directionality }], propDecorators: { nzSrc: [{
  950. type: Input
  951. }], nzSrcset: [{
  952. type: Input
  953. }], nzDisablePreview: [{
  954. type: Input,
  955. args: [{ transform: booleanAttribute }]
  956. }], nzFallback: [{
  957. type: Input
  958. }], nzPlaceholder: [{
  959. type: Input
  960. }], nzScaleStep: [{
  961. type: Input
  962. }] } });
  963. /**
  964. * Use of this source code is governed by an MIT-style license that can be
  965. * found in the LICENSE file at https://github.com/NG-ZORRO/ng-zorro-antd/blob/master/LICENSE
  966. */
  967. class NzImageModule {
  968. static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.2", ngImport: i0, type: NzImageModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule });
  969. static ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "19.2.2", ngImport: i0, type: NzImageModule, imports: [NzImageDirective, NzImagePreviewComponent, NzImageGroupComponent], exports: [NzImageDirective, NzImagePreviewComponent, NzImageGroupComponent] });
  970. static ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "19.2.2", ngImport: i0, type: NzImageModule, providers: [NzImageService], imports: [NzImagePreviewComponent] });
  971. }
  972. i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.2", ngImport: i0, type: NzImageModule, decorators: [{
  973. type: NgModule,
  974. args: [{
  975. imports: [NzImageDirective, NzImagePreviewComponent, NzImageGroupComponent],
  976. exports: [NzImageDirective, NzImagePreviewComponent, NzImageGroupComponent],
  977. providers: [NzImageService]
  978. }]
  979. }] });
  980. /**
  981. * Use of this source code is governed by an MIT-style license that can be
  982. * found in the LICENSE file at https://github.com/NG-ZORRO/ng-zorro-antd/blob/master/LICENSE
  983. */
  984. /**
  985. * Generated bundle index. Do not edit.
  986. */
  987. export { NZ_CONFIG_MODULE_NAME$1 as NZ_CONFIG_MODULE_NAME, NZ_DEFAULT_SCALE_STEP, NzImageDirective, NzImageGroupComponent, NzImageModule, NzImagePreviewComponent, NzImagePreviewOptions, NzImagePreviewRef, NzImageService, getClientSize, getFitContentPosition, getOffset };
  988. //# sourceMappingURL=ng-zorro-antd-image.mjs.map