UNPKG

@angular-mdc/web

Version:
582 lines (577 loc) 18.7 kB
/** * @license * Copyright (c) Dominic Carretto * * Use of this source code is governed by an MIT-style license that can be * found in the LICENSE file at https://github.com/trimox/angular-mdc-web/blob/master/LICENSE */ import { Component, ChangeDetectionStrategy, ViewEncapsulation, ElementRef, Input, EventEmitter, NgZone, ChangeDetectorRef, Optional, Inject, Output, ContentChild, Directive, NgModule } from '@angular/core'; import { DOCUMENT, CommonModule } from '@angular/common'; import { FocusTrapFactory, FocusMonitor } from '@angular/cdk/a11y'; import { coerceBooleanProperty } from '@angular/cdk/coercion'; import { Platform } from '@angular/cdk/platform'; import { Subject, fromEvent } from 'rxjs'; import { takeUntil } from 'rxjs/operators'; import { MDCComponent } from '@angular-mdc/web/base'; import { MdcList } from '@angular-mdc/web/list'; import { MDCModalDrawerFoundation, MDCDismissibleDrawerFoundation, cssClasses } from '@material/drawer'; /** * @fileoverview added by tsickle * Generated from: drawer/drawer.ts * @suppress {checkTypes,constantProperty,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc */ class MdcDrawerHeader { /** * @param {?} elementRef */ constructor(elementRef) { this.elementRef = elementRef; } } MdcDrawerHeader.decorators = [ { type: Component, args: [{selector: 'mdc-drawer-header', template: ` <ng-content></ng-content> <h3 class="mdc-drawer__title" *ngIf="title">{{title}}</h3> <h6 class="mdc-drawer__subtitle" *ngIf="subtitle">{{subtitle}}</h6>`, host: { 'class': 'mdc-drawer__header' }, changeDetection: ChangeDetectionStrategy.OnPush, encapsulation: ViewEncapsulation.None },] }, ]; /** @nocollapse */ MdcDrawerHeader.ctorParameters = () => [ { type: ElementRef } ]; MdcDrawerHeader.propDecorators = { title: [{ type: Input }], subtitle: [{ type: Input }] }; class MdcDrawer extends MDCComponent { /** * @param {?} _platform * @param {?} _ngZone * @param {?} _changeDetectorRef * @param {?} _focusTrapFactory * @param {?} _focusMonitor * @param {?} _document * @param {?} elementRef */ constructor(_platform, _ngZone, _changeDetectorRef, _focusTrapFactory, _focusMonitor, _document, elementRef) { super(elementRef); this._platform = _platform; this._ngZone = _ngZone; this._changeDetectorRef = _changeDetectorRef; this._focusTrapFactory = _focusTrapFactory; this._focusMonitor = _focusMonitor; this._document = _document; this.elementRef = elementRef; /** * Emits when the component is destroyed. */ this._destroyed = new Subject(); this._scrimElement = null; /** * Element that was focused before the drawer was opened. Save this to restore upon close. */ this._elementFocusedBeforeDrawerWasOpened = null; this._open = false; this._drawer = ''; this._autoFocus = true; this._restoreFocus = true; this.opened = new EventEmitter(); this.closed = new EventEmitter(); /** * Event emitted when the drawer open state is changed. */ this.openedChange = new EventEmitter(/* isAsync */ true); /** * Event emitted when the drawer variant is changed. */ this.drawerChange = new EventEmitter(/* isAsync */ true); this._scrimSubscription = null; this.openedChange.subscribe((/** * @param {?} opened * @return {?} */ (opened) => { if (opened) { if (this._document) { this._elementFocusedBeforeDrawerWasOpened = (/** @type {?} */ (this._document.activeElement)); } if (this._isFocusTrapEnabled && this._focusTrap) { this._trapFocus(); } } else { this._releaseFocus(); } })); this.drawerChange.subscribe((/** * @return {?} */ () => this._initFoundation())); /** * Listen to `keydown` events outside the zone so that change detection is not run every * time a key is pressed. Instead we re-enter the zone only if the `ESC` key is pressed * and we don't have close disabled. */ this._ngZone.runOutsideAngular((/** * @return {?} */ () => { ((/** @type {?} */ (fromEvent(this._elementRef.nativeElement, 'keydown')))) .pipe(takeUntil(this._destroyed)).subscribe((/** * @param {?} event * @return {?} */ event => this._ngZone.run((/** * @return {?} */ () => { this._foundation.handleKeydown(event); if (this.modal) { event.stopPropagation(); event.preventDefault(); } })))); })); } /** * @return {?} */ get open() { return this._open; } /** * @param {?} value * @return {?} */ set open(value) { if (this._platform.isBrowser && this._open !== value) { this._open = coerceBooleanProperty(value); this._open ? this._foundation.open() : this._foundation.close(); this.openedChange.emit(this._open); this._updateFocusTrapState(); this._changeDetectorRef.markForCheck(); } } /** * @return {?} */ get drawer() { return this._drawer; } /** * @param {?} drawer * @return {?} */ set drawer(drawer) { if (this._drawer !== drawer) { this._drawer = drawer; this.drawerChange.emit(); this._updateFocusTrapState(); } } /** * @return {?} */ get autoFocus() { return this._autoFocus; } /** * @param {?} value * @return {?} */ set autoFocus(value) { this._autoFocus = coerceBooleanProperty(value); } /** * @return {?} */ get restoreFocus() { return this._restoreFocus; } /** * @param {?} value * @return {?} */ set restoreFocus(value) { this._restoreFocus = coerceBooleanProperty(value); } /** * @return {?} */ get fixedAdjustElement() { return this._fixedAdjustElement; } /** * @param {?} element * @return {?} */ set fixedAdjustElement(element) { this._fixedAdjustElement = element; element ? this._getHostElement().style.setProperty('position', 'absolute') : this._getHostElement().style.removeProperty('position'); this._changeDetectorRef.markForCheck(); } /** * @return {?} */ get modal() { return this.drawer === 'modal'; } /** * @return {?} */ get dismissible() { return this.drawer === 'dismissible'; } /** * @return {?} */ get _isFocusTrapEnabled() { // The focus trap is only enabled when the drawer is open and modal. return this.open && this.modal; } /** * @return {?} */ getDefaultFoundation() { /** @type {?} */ const adapter = { addClass: (/** * @param {?} className * @return {?} */ (className) => this._getHostElement().classList.add(className)), removeClass: (/** * @param {?} className * @return {?} */ (className) => this._getHostElement().classList.remove(className)), hasClass: (/** * @param {?} className * @return {?} */ (className) => this._getHostElement().classList.contains(className)), elementHasClass: (/** * @param {?} element * @param {?} className * @return {?} */ (element, className) => element.classList.contains(className)), saveFocus: (/** * @return {?} */ () => this._savePreviouslyFocusedElement()), restoreFocus: (/** * @return {?} */ () => this._releaseFocus()), focusActiveNavigationItem: (/** * @return {?} */ () => { var _a; if (!this._platform.isBrowser || !this._list || !this._autoFocus) { return; } /** @type {?} */ const selectedItem = this._list.getSelectedItem(); if (selectedItem) { selectedItem.focus(); } else { /** @type {?} */ const cdkInitialItem = this._platform.isBrowser ? (/** @type {?} */ (document.querySelector(`[cdkFocusInitial]`))) : null; (_a = cdkInitialItem) === null || _a === void 0 ? void 0 : _a.focus(); } }), notifyClose: (/** * @return {?} */ () => this.closed.emit()), notifyOpen: (/** * @return {?} */ () => this.opened.emit()), trapFocus: (/** * @return {?} */ () => { }), releaseFocus: (/** * @return {?} */ () => this._releaseFocus()) }; return this.modal ? new MDCModalDrawerFoundation(adapter) : new MDCDismissibleDrawerFoundation(adapter); } /** * @return {?} */ ngAfterContentInit() { this._initListType(); } /** * @return {?} */ ngOnDestroy() { var _a, _b, _c; this.open = false; (_a = this._focusTrap) === null || _a === void 0 ? void 0 : _a.destroy(); (_b = this._scrimElement) === null || _b === void 0 ? void 0 : _b.remove(); (_c = this._scrimSubscription) === null || _c === void 0 ? void 0 : _c.unsubscribe(); this._destroyed.next(); this._destroyed.complete(); if (this._foundation && this._platform.isBrowser) { this._foundation.destroy(); } } /** * @param {?} event * @return {?} */ _handleTransitionEnd(event) { this._foundation.handleTransitionEnd(event); } /** * @private * @return {?} */ _createScrim() { if (this._platform.isBrowser) { this._scrimElement = document.createElement('div'); this._scrimElement.classList.add('mdc-drawer-scrim'); this._getHostElement().insertAdjacentElement('afterend', this._scrimElement); this._scrimSubscription = this._ngZone.runOutsideAngular((/** * @return {?} */ () => fromEvent((/** @type {?} */ (this._scrimElement)), 'click') .subscribe((/** * @return {?} */ () => this._ngZone.run((/** * @return {?} */ () => this.open = false)))))); } } /** * @private * @return {?} */ _initFoundation() { this._getHostElement().classList.remove(cssClasses.MODAL); this._getHostElement().classList.remove(cssClasses.DISMISSIBLE); this._foundation = this.getDefaultFoundation(); this._foundation.init(); if (this.modal || this.dismissible) { this._getHostElement().classList.add(`${cssClasses.ROOT}--${this.drawer}`); } if (this._scrimElement) { if (this._scrimSubscription) { this._scrimSubscription.unsubscribe(); } this._scrimElement.remove(); this._scrimElement = null; } if (this.modal) { this._focusTrap = this._focusTrapFactory.create(this._elementRef.nativeElement); this._updateFocusTrapState(); this._createScrim(); } else if (this._focusTrap) { this._focusTrap.destroy(); } this._changeDetectorRef.markForCheck(); } /** * @private * @return {?} */ _initListType() { if (this._list && (this._list.singleSelection || this._list.singleSelection === undefined)) { this._list.wrapFocus = true; this._list.singleSelection = true; this._list.useActivatedClass = true; } } /** * Updates the enabled state of the focus trap. * @private * @return {?} */ _updateFocusTrapState() { if (this._focusTrap) { this._focusTrap.enabled = this._isFocusTrapEnabled; } } /** * @private * @return {?} */ _trapFocus() { if (!this.autoFocus) { return; } this._focusTrap.focusInitialElementWhenReady().then((/** * @param {?} hasMovedFocus * @return {?} */ hasMovedFocus => { // If there were no focusable elements, focus the drawer itself so the keyboard navigation // still works. We need to check that `focus` is a function due to Universal. if (!hasMovedFocus && typeof this._elementRef.nativeElement.focus === 'function') { this._elementRef.nativeElement.focus(); } })); } /** * Restores focus to the element that was focused before the drawer opened. * @private * @return {?} */ _releaseFocus() { if (!this.autoFocus) { return; } /** @type {?} */ const activeEl = this._document && this._document.activeElement; if (activeEl && this._elementRef.nativeElement.contains(activeEl)) { if (this._elementFocusedBeforeDrawerWasOpened instanceof HTMLElement) { this._focusMonitor.focusVia(this._elementFocusedBeforeDrawerWasOpened, this._openedVia); } else { this._elementRef.nativeElement.blur(); } } this._elementFocusedBeforeDrawerWasOpened = null; this._openedVia = null; } /** * Saves a reference to the element that was focused before the drawer was opened. * @private * @return {?} */ _savePreviouslyFocusedElement() { if (this._document) { this._elementFocusedBeforeDrawerWasOpened = (/** @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 drawer immediately. Needs to be async, because the element // may not be focusable immediately. Promise.resolve().then((/** * @return {?} */ () => this._elementRef.nativeElement.focus())); } } } /** * @private * @return {?} */ _getHostElement() { return this.elementRef.nativeElement; } } MdcDrawer.decorators = [ { type: Component, args: [{selector: 'mdc-drawer', exportAs: 'mdcDrawer', host: { 'role': 'navigation', 'class': 'mdc-drawer', '(transitionend)': '_handleTransitionEnd($event)' }, template: '<ng-content></ng-content>', changeDetection: ChangeDetectionStrategy.OnPush, encapsulation: ViewEncapsulation.None },] }, ]; /** @nocollapse */ MdcDrawer.ctorParameters = () => [ { type: Platform }, { type: NgZone }, { type: ChangeDetectorRef }, { type: FocusTrapFactory }, { type: FocusMonitor }, { type: undefined, decorators: [{ type: Optional }, { type: Inject, args: [DOCUMENT,] }] }, { type: ElementRef } ]; MdcDrawer.propDecorators = { open: [{ type: Input }], drawer: [{ type: Input }], autoFocus: [{ type: Input }], restoreFocus: [{ type: Input }], fixedAdjustElement: [{ type: Input }], opened: [{ type: Output }], closed: [{ type: Output }], openedChange: [{ type: Output }], drawerChange: [{ type: Output }], _list: [{ type: ContentChild, args: [MdcList, { static: false },] }] }; /** * @fileoverview added by tsickle * Generated from: drawer/drawer-directives.ts * @suppress {checkTypes,constantProperty,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc */ class MdcDrawerTitle { } MdcDrawerTitle.decorators = [ { type: Directive, args: [{ selector: '[mdcDrawerTitle]', host: { 'class': 'mdc-drawer__title' } },] }, ]; class MdcDrawerSubtitle { } MdcDrawerSubtitle.decorators = [ { type: Directive, args: [{ selector: '[mdcDrawerSubtitle]', host: { 'class': 'mdc-drawer__subtitle' } },] }, ]; class MdcDrawerContent { } MdcDrawerContent.decorators = [ { type: Directive, args: [{ selector: 'mdc-drawer-content, [mdcDrawerContent]', host: { 'class': 'mdc-drawer__content' } },] }, ]; class MdcDrawerAppContent { } MdcDrawerAppContent.decorators = [ { type: Directive, args: [{ selector: 'mdc-drawer-app-content, [mdcDrawerAppContent]', host: { 'class': 'mdc-drawer-app-content' } },] }, ]; /** * @fileoverview added by tsickle * Generated from: drawer/module.ts * @suppress {checkTypes,constantProperty,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc */ /** @type {?} */ const DRAWER_DECLARATIONS = [ MdcDrawer, MdcDrawerAppContent, MdcDrawerContent, MdcDrawerHeader, MdcDrawerSubtitle, MdcDrawerTitle ]; class MdcDrawerModule { } MdcDrawerModule.decorators = [ { type: NgModule, args: [{ imports: [CommonModule], exports: [DRAWER_DECLARATIONS], declarations: [DRAWER_DECLARATIONS] },] }, ]; export { MdcDrawer, MdcDrawerAppContent, MdcDrawerContent, MdcDrawerHeader, MdcDrawerModule, MdcDrawerSubtitle, MdcDrawerTitle }; //# sourceMappingURL=drawer.js.map