| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386 |
- /**
- * @license
- * Copyright 2017 Google Inc.
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- * THE SOFTWARE.
- */
- import { __assign, __extends } from "tslib";
- import { AnimationFrame } from '@material/animation/animationframe';
- import { MDCFoundation } from '@material/base/foundation';
- import { cssClasses, numbers, strings } from './constants';
- var AnimationKeys;
- (function (AnimationKeys) {
- AnimationKeys["POLL_SCROLL_POS"] = "poll_scroll_position";
- AnimationKeys["POLL_LAYOUT_CHANGE"] = "poll_layout_change";
- })(AnimationKeys || (AnimationKeys = {}));
- /** MDC Dialog Foundation */
- var MDCDialogFoundation = /** @class */ (function (_super) {
- __extends(MDCDialogFoundation, _super);
- function MDCDialogFoundation(adapter) {
- var _this = _super.call(this, __assign(__assign({}, MDCDialogFoundation.defaultAdapter), adapter)) || this;
- _this.dialogOpen = false;
- _this.isFullscreen = false;
- _this.animationFrame = 0;
- _this.animationTimer = 0;
- _this.escapeKeyAction = strings.CLOSE_ACTION;
- _this.scrimClickAction = strings.CLOSE_ACTION;
- _this.autoStackButtons = true;
- _this.areButtonsStacked = false;
- _this.suppressDefaultPressSelector = strings.SUPPRESS_DEFAULT_PRESS_SELECTOR;
- _this.animFrame = new AnimationFrame();
- _this.contentScrollHandler = function () {
- _this.handleScrollEvent();
- };
- _this.windowResizeHandler = function () {
- _this.layout();
- };
- _this.windowOrientationChangeHandler = function () {
- _this.layout();
- };
- return _this;
- }
- Object.defineProperty(MDCDialogFoundation, "cssClasses", {
- get: function () {
- return cssClasses;
- },
- enumerable: false,
- configurable: true
- });
- Object.defineProperty(MDCDialogFoundation, "strings", {
- get: function () {
- return strings;
- },
- enumerable: false,
- configurable: true
- });
- Object.defineProperty(MDCDialogFoundation, "numbers", {
- get: function () {
- return numbers;
- },
- enumerable: false,
- configurable: true
- });
- Object.defineProperty(MDCDialogFoundation, "defaultAdapter", {
- get: function () {
- return {
- addBodyClass: function () { return undefined; },
- addClass: function () { return undefined; },
- areButtonsStacked: function () { return false; },
- clickDefaultButton: function () { return undefined; },
- eventTargetMatches: function () { return false; },
- getActionFromEvent: function () { return ''; },
- getInitialFocusEl: function () { return null; },
- hasClass: function () { return false; },
- isContentScrollable: function () { return false; },
- notifyClosed: function () { return undefined; },
- notifyClosing: function () { return undefined; },
- notifyOpened: function () { return undefined; },
- notifyOpening: function () { return undefined; },
- releaseFocus: function () { return undefined; },
- removeBodyClass: function () { return undefined; },
- removeClass: function () { return undefined; },
- reverseButtons: function () { return undefined; },
- trapFocus: function () { return undefined; },
- registerContentEventHandler: function () { return undefined; },
- deregisterContentEventHandler: function () { return undefined; },
- isScrollableContentAtTop: function () { return false; },
- isScrollableContentAtBottom: function () { return false; },
- registerWindowEventHandler: function () { return undefined; },
- deregisterWindowEventHandler: function () { return undefined; },
- };
- },
- enumerable: false,
- configurable: true
- });
- MDCDialogFoundation.prototype.init = function () {
- if (this.adapter.hasClass(cssClasses.STACKED)) {
- this.setAutoStackButtons(false);
- }
- this.isFullscreen = this.adapter.hasClass(cssClasses.FULLSCREEN);
- };
- MDCDialogFoundation.prototype.destroy = function () {
- if (this.animationTimer) {
- clearTimeout(this.animationTimer);
- this.handleAnimationTimerEnd();
- }
- if (this.isFullscreen) {
- this.adapter.deregisterContentEventHandler('scroll', this.contentScrollHandler);
- }
- this.animFrame.cancelAll();
- this.adapter.deregisterWindowEventHandler('resize', this.windowResizeHandler);
- this.adapter.deregisterWindowEventHandler('orientationchange', this.windowOrientationChangeHandler);
- };
- MDCDialogFoundation.prototype.open = function (dialogOptions) {
- var _this = this;
- this.dialogOpen = true;
- this.adapter.notifyOpening();
- this.adapter.addClass(cssClasses.OPENING);
- if (this.isFullscreen) {
- // A scroll event listener is registered even if the dialog is not
- // scrollable on open, since the window resize event, or orientation
- // change may make the dialog scrollable after it is opened.
- this.adapter.registerContentEventHandler('scroll', this.contentScrollHandler);
- }
- if (dialogOptions && dialogOptions.isAboveFullscreenDialog) {
- this.adapter.addClass(cssClasses.SCRIM_HIDDEN);
- }
- this.adapter.registerWindowEventHandler('resize', this.windowResizeHandler);
- this.adapter.registerWindowEventHandler('orientationchange', this.windowOrientationChangeHandler);
- // Wait a frame once display is no longer "none", to establish basis for
- // animation
- this.runNextAnimationFrame(function () {
- _this.adapter.addClass(cssClasses.OPEN);
- if (!dialogOptions || !dialogOptions.isScrimless) {
- _this.adapter.addBodyClass(cssClasses.SCROLL_LOCK);
- }
- _this.layout();
- _this.animationTimer = setTimeout(function () {
- _this.handleAnimationTimerEnd();
- _this.adapter.trapFocus(_this.adapter.getInitialFocusEl());
- _this.adapter.notifyOpened();
- }, numbers.DIALOG_ANIMATION_OPEN_TIME_MS);
- });
- };
- MDCDialogFoundation.prototype.close = function (action) {
- var _this = this;
- if (action === void 0) { action = ''; }
- if (!this.dialogOpen) {
- // Avoid redundant close calls (and events), e.g. from keydown on elements
- // that inherently emit click
- return;
- }
- this.dialogOpen = false;
- this.adapter.notifyClosing(action);
- this.adapter.addClass(cssClasses.CLOSING);
- this.adapter.removeClass(cssClasses.OPEN);
- this.adapter.removeBodyClass(cssClasses.SCROLL_LOCK);
- if (this.isFullscreen) {
- this.adapter.deregisterContentEventHandler('scroll', this.contentScrollHandler);
- }
- this.adapter.deregisterWindowEventHandler('resize', this.windowResizeHandler);
- this.adapter.deregisterWindowEventHandler('orientationchange', this.windowOrientationChangeHandler);
- cancelAnimationFrame(this.animationFrame);
- this.animationFrame = 0;
- clearTimeout(this.animationTimer);
- this.animationTimer = setTimeout(function () {
- _this.adapter.releaseFocus();
- _this.handleAnimationTimerEnd();
- _this.adapter.notifyClosed(action);
- }, numbers.DIALOG_ANIMATION_CLOSE_TIME_MS);
- };
- /**
- * Used only in instances of showing a secondary dialog over a full-screen
- * dialog. Shows the "surface scrim" displayed over the full-screen dialog.
- */
- MDCDialogFoundation.prototype.showSurfaceScrim = function () {
- var _this = this;
- this.adapter.addClass(cssClasses.SURFACE_SCRIM_SHOWING);
- this.runNextAnimationFrame(function () {
- _this.adapter.addClass(cssClasses.SURFACE_SCRIM_SHOWN);
- });
- };
- /**
- * Used only in instances of showing a secondary dialog over a full-screen
- * dialog. Hides the "surface scrim" displayed over the full-screen dialog.
- */
- MDCDialogFoundation.prototype.hideSurfaceScrim = function () {
- this.adapter.removeClass(cssClasses.SURFACE_SCRIM_SHOWN);
- this.adapter.addClass(cssClasses.SURFACE_SCRIM_HIDING);
- };
- /**
- * Handles `transitionend` event triggered when surface scrim animation is
- * finished.
- */
- MDCDialogFoundation.prototype.handleSurfaceScrimTransitionEnd = function () {
- this.adapter.removeClass(cssClasses.SURFACE_SCRIM_HIDING);
- this.adapter.removeClass(cssClasses.SURFACE_SCRIM_SHOWING);
- };
- MDCDialogFoundation.prototype.isOpen = function () {
- return this.dialogOpen;
- };
- MDCDialogFoundation.prototype.getEscapeKeyAction = function () {
- return this.escapeKeyAction;
- };
- MDCDialogFoundation.prototype.setEscapeKeyAction = function (action) {
- this.escapeKeyAction = action;
- };
- MDCDialogFoundation.prototype.getScrimClickAction = function () {
- return this.scrimClickAction;
- };
- MDCDialogFoundation.prototype.setScrimClickAction = function (action) {
- this.scrimClickAction = action;
- };
- MDCDialogFoundation.prototype.getAutoStackButtons = function () {
- return this.autoStackButtons;
- };
- MDCDialogFoundation.prototype.setAutoStackButtons = function (autoStack) {
- this.autoStackButtons = autoStack;
- };
- MDCDialogFoundation.prototype.getSuppressDefaultPressSelector = function () {
- return this.suppressDefaultPressSelector;
- };
- MDCDialogFoundation.prototype.setSuppressDefaultPressSelector = function (selector) {
- this.suppressDefaultPressSelector = selector;
- };
- MDCDialogFoundation.prototype.layout = function () {
- var _this = this;
- this.animFrame.request(AnimationKeys.POLL_LAYOUT_CHANGE, function () {
- _this.layoutInternal();
- });
- };
- /** Handles click on the dialog root element. */
- MDCDialogFoundation.prototype.handleClick = function (evt) {
- var isScrim = this.adapter.eventTargetMatches(evt.target, strings.SCRIM_SELECTOR);
- // Check for scrim click first since it doesn't require querying ancestors.
- if (isScrim && this.scrimClickAction !== '') {
- this.close(this.scrimClickAction);
- }
- else {
- var action = this.adapter.getActionFromEvent(evt);
- if (action) {
- this.close(action);
- }
- }
- };
- /** Handles keydown on the dialog root element. */
- MDCDialogFoundation.prototype.handleKeydown = function (evt) {
- var isEnter = evt.key === 'Enter' || evt.keyCode === 13;
- if (!isEnter) {
- return;
- }
- var action = this.adapter.getActionFromEvent(evt);
- if (action) {
- // Action button callback is handled in `handleClick`,
- // since space/enter keydowns on buttons trigger click events.
- return;
- }
- // `composedPath` is used here, when available, to account for use cases
- // where a target meant to suppress the default press behaviour
- // may exist in a shadow root.
- // For example, a textarea inside a web component:
- // <mwc-dialog>
- // <horizontal-layout>
- // #shadow-root (open)
- // <mwc-textarea>
- // #shadow-root (open)
- // <textarea></textarea>
- // </mwc-textarea>
- // </horizontal-layout>
- // </mwc-dialog>
- var target = evt.composedPath ? evt.composedPath()[0] : evt.target;
- var isDefault = this.suppressDefaultPressSelector ?
- !this.adapter.eventTargetMatches(target, this.suppressDefaultPressSelector) :
- true;
- if (isEnter && isDefault) {
- this.adapter.clickDefaultButton();
- }
- };
- /** Handles keydown on the document. */
- MDCDialogFoundation.prototype.handleDocumentKeydown = function (evt) {
- var isEscape = evt.key === 'Escape' || evt.keyCode === 27;
- if (isEscape && this.escapeKeyAction !== '') {
- this.close(this.escapeKeyAction);
- }
- };
- /**
- * Handles scroll event on the dialog's content element -- showing a scroll
- * divider on the header or footer based on the scroll position. This handler
- * should only be registered on full-screen dialogs with scrollable content.
- */
- MDCDialogFoundation.prototype.handleScrollEvent = function () {
- var _this = this;
- // Since scroll events can fire at a high rate, we throttle these events by
- // using requestAnimationFrame.
- this.animFrame.request(AnimationKeys.POLL_SCROLL_POS, function () {
- _this.toggleScrollDividerHeader();
- _this.toggleScrollDividerFooter();
- });
- };
- MDCDialogFoundation.prototype.layoutInternal = function () {
- if (this.autoStackButtons) {
- this.detectStackedButtons();
- }
- this.toggleScrollableClasses();
- };
- MDCDialogFoundation.prototype.handleAnimationTimerEnd = function () {
- this.animationTimer = 0;
- this.adapter.removeClass(cssClasses.OPENING);
- this.adapter.removeClass(cssClasses.CLOSING);
- };
- /**
- * Runs the given logic on the next animation frame, using setTimeout to
- * factor in Firefox reflow behavior.
- */
- MDCDialogFoundation.prototype.runNextAnimationFrame = function (callback) {
- var _this = this;
- cancelAnimationFrame(this.animationFrame);
- this.animationFrame = requestAnimationFrame(function () {
- _this.animationFrame = 0;
- clearTimeout(_this.animationTimer);
- _this.animationTimer = setTimeout(callback, 0);
- });
- };
- MDCDialogFoundation.prototype.detectStackedButtons = function () {
- // Remove the class first to let us measure the buttons' natural positions.
- this.adapter.removeClass(cssClasses.STACKED);
- var areButtonsStacked = this.adapter.areButtonsStacked();
- if (areButtonsStacked) {
- this.adapter.addClass(cssClasses.STACKED);
- }
- if (areButtonsStacked !== this.areButtonsStacked) {
- this.adapter.reverseButtons();
- this.areButtonsStacked = areButtonsStacked;
- }
- };
- MDCDialogFoundation.prototype.toggleScrollableClasses = function () {
- // Remove the class first to let us measure the natural height of the
- // content.
- this.adapter.removeClass(cssClasses.SCROLLABLE);
- if (this.adapter.isContentScrollable()) {
- this.adapter.addClass(cssClasses.SCROLLABLE);
- if (this.isFullscreen) {
- // If dialog is full-screen and scrollable, check if a scroll divider
- // should be shown.
- this.toggleScrollDividerHeader();
- this.toggleScrollDividerFooter();
- }
- }
- };
- MDCDialogFoundation.prototype.toggleScrollDividerHeader = function () {
- if (!this.adapter.isScrollableContentAtTop()) {
- this.adapter.addClass(cssClasses.SCROLL_DIVIDER_HEADER);
- }
- else if (this.adapter.hasClass(cssClasses.SCROLL_DIVIDER_HEADER)) {
- this.adapter.removeClass(cssClasses.SCROLL_DIVIDER_HEADER);
- }
- };
- MDCDialogFoundation.prototype.toggleScrollDividerFooter = function () {
- if (!this.adapter.isScrollableContentAtBottom()) {
- this.adapter.addClass(cssClasses.SCROLL_DIVIDER_FOOTER);
- }
- else if (this.adapter.hasClass(cssClasses.SCROLL_DIVIDER_FOOTER)) {
- this.adapter.removeClass(cssClasses.SCROLL_DIVIDER_FOOTER);
- }
- };
- return MDCDialogFoundation;
- }(MDCFoundation));
- export { MDCDialogFoundation };
- // tslint:disable-next-line:no-default-export Needed for backward compatibility with MDC Web v0.44.0 and earlier.
- export default MDCDialogFoundation;
- //# sourceMappingURL=foundation.js.map
|