UNPKG

@ngx-gallery/lightbox

Version:

Angular gallery directive that hooks the lightbox with the images automatically.

433 lines (422 loc) 17.4 kB
import { DomSanitizer } from '@angular/platform-browser'; import { DOCUMENT } from '@angular/common'; import { animate, state, style, transition, trigger } from '@angular/animations'; import { __assign } from 'tslib'; import { ComponentPortal } from '@angular/cdk/portal'; import { LEFT_ARROW, RIGHT_ARROW, ESCAPE } from '@angular/cdk/keycodes'; import { Overlay, OverlayModule } from '@angular/cdk/overlay'; import { FocusTrapFactory, A11yModule } from '@angular/cdk/a11y'; import { Gallery, GalleryModule } from '@ngx-gallery/core'; import { InjectionToken, Component, Optional, ChangeDetectionStrategy, ElementRef, Inject, Injectable, Directive, Input, Renderer2, NgModule } from '@angular/core'; import { Subject, fromEvent, Subscription } from 'rxjs'; import { tap } from 'rxjs/operators'; /** * @fileoverview added by tsickle * @suppress {checkTypes,extraRequire,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc */ /** @type {?} */ var LIGHTBOX_CONFIG = new InjectionToken('lightboxConfig'); /** * @fileoverview added by tsickle * @suppress {checkTypes,extraRequire,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc */ /** @type {?} */ var lightboxAnimation = trigger('lightbox', [ // Note: The `enter` animation transitions to `transform: none`, because for some reason // specifying the transform explicitly, causes IE both to blur the dialog content and // decimate the animation performance. Leaving it as `none` solves both issues. state('void, exit', style({ opacity: 0, transform: 'scale(0.7)' })), state('enter', style({ transform: 'none' })), transition('* => enter', animate('150ms cubic-bezier(0, 0, 0.2, 1)', style({ transform: 'none', opacity: 1 }))), transition('* => void, * => exit', animate('75ms cubic-bezier(0.4, 0.0, 0.2, 1)', style({ opacity: 0 }))), ]); /** * @fileoverview added by tsickle * @suppress {checkTypes,extraRequire,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc */ var LightboxComponent = /** @class */ (function () { function LightboxComponent(_document, _focusTrapFactory, _elementRef, sanitizer) { this._document = _document; this._focusTrapFactory = _focusTrapFactory; this._elementRef = _elementRef; this.sanitizer = sanitizer; /** * State of the lightbox animation. */ this.state = 'enter'; this._savePreviouslyFocusedElement(); } /** Callback, invoked whenever an animation on the host completes. */ /** * Callback, invoked whenever an animation on the host completes. * @param {?} event * @return {?} */ LightboxComponent.prototype.onAnimationDone = /** * Callback, invoked whenever an animation on the host completes. * @param {?} event * @return {?} */ function (event) { if (event.toState === 'enter') { this._trapFocus(); } else { this.overlayRef.dispose(); this._restoreFocus(); } }; /** Moves the focus inside the focus trap. */ /** * Moves the focus inside the focus trap. * @private * @return {?} */ LightboxComponent.prototype._trapFocus = /** * Moves the focus inside the focus trap. * @private * @return {?} */ function () { if (!this._focusTrap) { this._focusTrap = this._focusTrapFactory.create(this._elementRef.nativeElement); } // If were to attempt to focus immediately, then the content of the lightbox would not yet be // ready in instances where change detection has to run first. To deal with this, we simply // wait for the microtask queue to be empty. this._focusTrap.focusInitialElementWhenReady(); }; /** Saves a reference to the element that was focused before the lightbox was opened. */ /** * Saves a reference to the element that was focused before the lightbox was opened. * @private * @return {?} */ LightboxComponent.prototype._savePreviouslyFocusedElement = /** * Saves a reference to the element that was focused before the lightbox was opened. * @private * @return {?} */ function () { var _this = this; if (this._document) { this._elementFocusedBeforeDialogWasOpened = (/** @type {?} */ (this._document.activeElement)); // Note that there is no focus method when rendering on the server. if (this._elementRef.nativeElement.focus) { // Move focus onto the lightbox immediately in order to prevent the user from accidentally // opening multiple dialogs at the same time. Needs to be async, because the element // may not be focusable immediately. Promise.resolve().then(function () { return _this._elementRef.nativeElement.focus(); }); } } }; /** Restores focus to the element that was focused before the lightbox opened. */ /** * Restores focus to the element that was focused before the lightbox opened. * @private * @return {?} */ LightboxComponent.prototype._restoreFocus = /** * Restores focus to the element that was focused before the lightbox opened. * @private * @return {?} */ function () { /** @type {?} */ var toFocus = this._elementFocusedBeforeDialogWasOpened; // We need the extra check, because IE can set the `activeElement` to null in some cases. if (toFocus && typeof toFocus.focus === 'function') { toFocus.focus(); } if (this._focusTrap) { this._focusTrap.destroy(); } }; LightboxComponent.decorators = [ { type: Component, args: [{ selector: 'lightbox', changeDetection: ChangeDetectionStrategy.OnPush, animations: [lightboxAnimation], template: "\n <gallery [id]=\"id\" [destroyRef]=\"false\" [skipInitConfig]=\"true\">\n <i class=\"g-btn-close\" aria-label=\"Close\" (click)=\"overlayRef.detach()\"\n [innerHTML]=\"sanitizer.bypassSecurityTrustHtml(closeIcon)\"></i>\n </gallery>\n ", host: { 'tabindex': '-1', 'aria-modal': 'true', '[attr.id]': '"lightbox-" + id', '[attr.role]': 'role', '[attr.aria-labelledby]': 'ariaLabel ? null : ariaLabelledBy', '[attr.aria-label]': 'ariaLabel', '[attr.aria-describedby]': 'ariaDescribedBy || null', '[@lightbox]': 'state', '(@lightbox.done)': 'onAnimationDone($event)', }, styles: ["::ng-deep lightbox{position:relative;display:block;width:1100px;height:800px;max-width:94vw;max-height:90vh;border-radius:4px;overflow:hidden;box-shadow:0 11px 15px -7px rgba(0,0,0,.2),0 24px 38px 3px rgba(0,0,0,.14),0 9px 46px 8px rgba(0,0,0,.12)}::ng-deep lightbox:focus{outline:0}::ng-deep lightbox gallery{overflow:hidden;margin:0;display:block;width:100%;height:100%}::ng-deep .g-backdrop{background-color:rgba(0,0,0,.32)}::ng-deep .fullscreen{width:100%}::ng-deep .fullscreen ::ng-deep lightbox{max-width:unset;max-height:unset;position:fixed;top:0;left:0;bottom:0;right:0;height:100%;width:100%;border-radius:0}::ng-deep .g-overlay{margin:auto}@media only screen and (max-width:480px){::ng-deep .g-overlay{width:100%}::ng-deep .g-overlay ::ng-deep lightbox{max-width:unset;max-height:unset;position:fixed;top:0;left:0;bottom:0;right:0;height:100%;width:100%;border-radius:0}}::ng-deep .g-btn-close{position:absolute;right:.9em;top:.9em;z-index:60;cursor:pointer;width:20px;height:20px}@media only screen and (max-width:480px){::ng-deep .g-btn-close{right:.7em;top:.7em}}"] }] } ]; /** @nocollapse */ LightboxComponent.ctorParameters = function () { return [ { type: undefined, decorators: [{ type: Optional }, { type: Inject, args: [DOCUMENT,] }] }, { type: FocusTrapFactory }, { type: ElementRef }, { type: DomSanitizer } ]; }; return LightboxComponent; }()); /** * @fileoverview added by tsickle * @suppress {checkTypes,extraRequire,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc */ /** @type {?} */ var defaultConfig = { backdropClass: 'g-backdrop', panelClass: 'g-overlay', hasBackdrop: true, keyboardShortcuts: true, role: 'lightbox', closeIcon: "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<svg width=\"512px\" height=\"512px\" enable-background=\"new 0 0 47.971 47.971\" version=\"1.1\" viewBox=\"0 0 47.971 47.971\" xml:space=\"preserve\" xmlns=\"http://www.w3.org/2000/svg\">\n\t<path d=\"M28.228,23.986L47.092,5.122c1.172-1.171,1.172-3.071,0-4.242c-1.172-1.172-3.07-1.172-4.242,0L23.986,19.744L5.121,0.88 c-1.172-1.172-3.07-1.172-4.242,0c-1.172,1.171-1.172,3.071,0,4.242l18.865,18.864L0.879,42.85c-1.172,1.171-1.172,3.071,0,4.242 C1.465,47.677,2.233,47.97,3,47.97s1.535-0.293,2.121-0.879l18.865-18.864L42.85,47.091c0.586,0.586,1.354,0.879,2.121,0.879 s1.535-0.293,2.121-0.879c1.172-1.171,1.172-3.071,0-4.242L28.228,23.986z\" fill=\"#fff\"/>\n</svg>\n" }; /** * @fileoverview added by tsickle * @suppress {checkTypes,extraRequire,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc */ var Lightbox = /** @class */ (function () { function Lightbox(config, _gallery, _overlay) { this._gallery = _gallery; this._overlay = _overlay; /** * Stream that emits when lightbox is opened */ this.opened = new Subject(); /** * Stream that emits when lightbox is closed */ this.closed = new Subject(); this._config = config ? __assign({}, defaultConfig, config) : defaultConfig; } /** * Set Lightbox Config * @param config - LightboxConfig */ /** * Set Lightbox Config * @param {?} config - LightboxConfig * @return {?} */ Lightbox.prototype.setConfig = /** * Set Lightbox Config * @param {?} config - LightboxConfig * @return {?} */ function (config) { this._config = __assign({}, this._config, config); }; /** * Open Lightbox Overlay * @param i - Current Index * @param id - Gallery ID * @param config - Lightbox Config */ /** * Open Lightbox Overlay * @param {?=} i - Current Index * @param {?=} id - Gallery ID * @param {?=} config - Lightbox Config * @return {?} */ Lightbox.prototype.open = /** * Open Lightbox Overlay * @param {?=} i - Current Index * @param {?=} id - Gallery ID * @param {?=} config - Lightbox Config * @return {?} */ function (i, id, config) { var _this = this; if (i === void 0) { i = 0; } if (id === void 0) { id = 'lightbox'; } /** @type {?} */ var _config = config ? __assign({}, this._config, config) : this._config; /** @type {?} */ var overlayConfig = { backdropClass: _config.backdropClass, panelClass: _config.panelClass, hasBackdrop: _config.hasBackdrop, positionStrategy: this._overlay.position().global().centerHorizontally().centerVertically(), scrollStrategy: this._overlay.scrollStrategies.block(), disposeOnNavigation: true }; /** @type {?} */ var galleryRef = this._gallery.ref(id); galleryRef.set(i); this._overlayRef = this._overlay.create(overlayConfig); // overlay opened event this._overlayRef.attachments().subscribe(function () { return _this.opened.next(id); }); // overlay closed event this._overlayRef.detachments().subscribe(function () { return _this.closed.next(id); }); // Attach gallery to the overlay /** @type {?} */ var galleryPortal = new ComponentPortal(LightboxComponent); /** @type {?} */ var lightboxRef = this._overlayRef.attach(galleryPortal); lightboxRef.instance.id = id; lightboxRef.instance.overlayRef = this._overlayRef; lightboxRef.instance.closeIcon = this._config.closeIcon; lightboxRef.instance.role = this._config.role; lightboxRef.instance.ariaLabel = this._config.ariaLabel; lightboxRef.instance.ariaLabelledBy = this._config.ariaLabelledBy; lightboxRef.instance.ariaDescribedBy = this._config.ariaDescribedBy; if (_config.hasBackdrop) { this._overlayRef.backdropClick().subscribe(function () { return _this.close(); }); } // Add keyboard shortcuts if (_config.keyboardShortcuts) { this._overlayRef.keydownEvents().subscribe(function (event) { switch (event.keyCode) { case LEFT_ARROW: galleryRef.prev(); break; case RIGHT_ARROW: galleryRef.next(); break; case ESCAPE: _this.close(); } }); } }; /** * Close Lightbox Overlay */ /** * Close Lightbox Overlay * @return {?} */ Lightbox.prototype.close = /** * Close Lightbox Overlay * @return {?} */ function () { if (this._overlayRef.hasAttached()) { this._overlayRef.detach(); } }; Lightbox.decorators = [ { type: Injectable } ]; /** @nocollapse */ Lightbox.ctorParameters = function () { return [ { type: undefined, decorators: [{ type: Optional }, { type: Inject, args: [LIGHTBOX_CONFIG,] }] }, { type: Gallery }, { type: Overlay } ]; }; return Lightbox; }()); /** * @fileoverview added by tsickle * @suppress {checkTypes,extraRequire,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc */ var LightboxDirective = /** @class */ (function () { function LightboxDirective(_lightbox, _el, _renderer) { this._lightbox = _lightbox; this._el = _el; this._renderer = _renderer; this.clickEvent = Subscription.EMPTY; this.index = 0; this.id = 'root'; } /** * @return {?} */ LightboxDirective.prototype.ngOnInit = /** * @return {?} */ function () { var _this = this; this._renderer.setStyle(this._el.nativeElement, 'cursor', 'pointer'); this.clickEvent = fromEvent(this._el.nativeElement, 'click').pipe(tap(function () { return _this._lightbox.open(_this.index, _this.id); })).subscribe(); }; /** * @return {?} */ LightboxDirective.prototype.ngOnDestroy = /** * @return {?} */ function () { this.clickEvent.unsubscribe(); }; LightboxDirective.decorators = [ { type: Directive, args: [{ selector: '[lightbox]' },] } ]; /** @nocollapse */ LightboxDirective.ctorParameters = function () { return [ { type: Lightbox }, { type: ElementRef }, { type: Renderer2 } ]; }; LightboxDirective.propDecorators = { index: [{ type: Input, args: ['lightbox',] }], id: [{ type: Input, args: ['gallery',] }] }; return LightboxDirective; }()); /** * @fileoverview added by tsickle * @suppress {checkTypes,extraRequire,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc */ var LightboxModule = /** @class */ (function () { function LightboxModule() { } /** * @param {?} config * @return {?} */ LightboxModule.withConfig = /** * @param {?} config * @return {?} */ function (config) { return { ngModule: LightboxModule, providers: [ { provide: LIGHTBOX_CONFIG, useValue: config } ] }; }; LightboxModule.decorators = [ { type: NgModule, args: [{ imports: [ OverlayModule, GalleryModule, A11yModule ], declarations: [ LightboxComponent, LightboxDirective ], exports: [ LightboxDirective ], providers: [ Lightbox ], entryComponents: [ LightboxComponent ] },] } ]; return LightboxModule; }()); /** * @fileoverview added by tsickle * @suppress {checkTypes,extraRequire,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc */ /** * @fileoverview added by tsickle * @suppress {checkTypes,extraRequire,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc */ export { LIGHTBOX_CONFIG, LightboxComponent, Lightbox, LightboxModule, lightboxAnimation as ɵa, LightboxDirective as ɵb }; //# sourceMappingURL=ngx-gallery-lightbox.js.map