| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468 |
- /**
- * @license
- * Copyright 2016 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 * as tslib_1 from "tslib";
- import { MDCFoundation } from '@material/base/foundation';
- import { cssClasses, numbers, strings } from './constants';
- import { getNormalizedEventCoords } from './util';
- // Activation events registered on the root element of each instance for activation
- var ACTIVATION_EVENT_TYPES = [
- 'touchstart', 'pointerdown', 'mousedown', 'keydown',
- ];
- // Deactivation events registered on documentElement when a pointer-related down event occurs
- var POINTER_DEACTIVATION_EVENT_TYPES = [
- 'touchend', 'pointerup', 'mouseup', 'contextmenu',
- ];
- // simultaneous nested activations
- var activatedTargets = [];
- var MDCRippleFoundation = /** @class */ (function (_super) {
- tslib_1.__extends(MDCRippleFoundation, _super);
- function MDCRippleFoundation(adapter) {
- var _this = _super.call(this, tslib_1.__assign({}, MDCRippleFoundation.defaultAdapter, adapter)) || this;
- _this.activationAnimationHasEnded_ = false;
- _this.activationTimer_ = 0;
- _this.fgDeactivationRemovalTimer_ = 0;
- _this.fgScale_ = '0';
- _this.frame_ = { width: 0, height: 0 };
- _this.initialSize_ = 0;
- _this.layoutFrame_ = 0;
- _this.maxRadius_ = 0;
- _this.unboundedCoords_ = { left: 0, top: 0 };
- _this.activationState_ = _this.defaultActivationState_();
- _this.activationTimerCallback_ = function () {
- _this.activationAnimationHasEnded_ = true;
- _this.runDeactivationUXLogicIfReady_();
- };
- _this.activateHandler_ = function (e) { return _this.activate_(e); };
- _this.deactivateHandler_ = function () { return _this.deactivate_(); };
- _this.focusHandler_ = function () { return _this.handleFocus(); };
- _this.blurHandler_ = function () { return _this.handleBlur(); };
- _this.resizeHandler_ = function () { return _this.layout(); };
- return _this;
- }
- Object.defineProperty(MDCRippleFoundation, "cssClasses", {
- get: function () {
- return cssClasses;
- },
- enumerable: true,
- configurable: true
- });
- Object.defineProperty(MDCRippleFoundation, "strings", {
- get: function () {
- return strings;
- },
- enumerable: true,
- configurable: true
- });
- Object.defineProperty(MDCRippleFoundation, "numbers", {
- get: function () {
- return numbers;
- },
- enumerable: true,
- configurable: true
- });
- Object.defineProperty(MDCRippleFoundation, "defaultAdapter", {
- get: function () {
- return {
- addClass: function () { return undefined; },
- browserSupportsCssVars: function () { return true; },
- computeBoundingRect: function () { return ({ top: 0, right: 0, bottom: 0, left: 0, width: 0, height: 0 }); },
- containsEventTarget: function () { return true; },
- deregisterDocumentInteractionHandler: function () { return undefined; },
- deregisterInteractionHandler: function () { return undefined; },
- deregisterResizeHandler: function () { return undefined; },
- getWindowPageOffset: function () { return ({ x: 0, y: 0 }); },
- isSurfaceActive: function () { return true; },
- isSurfaceDisabled: function () { return true; },
- isUnbounded: function () { return true; },
- registerDocumentInteractionHandler: function () { return undefined; },
- registerInteractionHandler: function () { return undefined; },
- registerResizeHandler: function () { return undefined; },
- removeClass: function () { return undefined; },
- updateCssVariable: function () { return undefined; },
- };
- },
- enumerable: true,
- configurable: true
- });
- MDCRippleFoundation.prototype.init = function () {
- var _this = this;
- var supportsPressRipple = this.supportsPressRipple_();
- this.registerRootHandlers_(supportsPressRipple);
- if (supportsPressRipple) {
- var _a = MDCRippleFoundation.cssClasses, ROOT_1 = _a.ROOT, UNBOUNDED_1 = _a.UNBOUNDED;
- requestAnimationFrame(function () {
- _this.adapter_.addClass(ROOT_1);
- if (_this.adapter_.isUnbounded()) {
- _this.adapter_.addClass(UNBOUNDED_1);
- // Unbounded ripples need layout logic applied immediately to set coordinates for both shade and ripple
- _this.layoutInternal_();
- }
- });
- }
- };
- MDCRippleFoundation.prototype.destroy = function () {
- var _this = this;
- if (this.supportsPressRipple_()) {
- if (this.activationTimer_) {
- clearTimeout(this.activationTimer_);
- this.activationTimer_ = 0;
- this.adapter_.removeClass(MDCRippleFoundation.cssClasses.FG_ACTIVATION);
- }
- if (this.fgDeactivationRemovalTimer_) {
- clearTimeout(this.fgDeactivationRemovalTimer_);
- this.fgDeactivationRemovalTimer_ = 0;
- this.adapter_.removeClass(MDCRippleFoundation.cssClasses.FG_DEACTIVATION);
- }
- var _a = MDCRippleFoundation.cssClasses, ROOT_2 = _a.ROOT, UNBOUNDED_2 = _a.UNBOUNDED;
- requestAnimationFrame(function () {
- _this.adapter_.removeClass(ROOT_2);
- _this.adapter_.removeClass(UNBOUNDED_2);
- _this.removeCssVars_();
- });
- }
- this.deregisterRootHandlers_();
- this.deregisterDeactivationHandlers_();
- };
- /**
- * @param evt Optional event containing position information.
- */
- MDCRippleFoundation.prototype.activate = function (evt) {
- this.activate_(evt);
- };
- MDCRippleFoundation.prototype.deactivate = function () {
- this.deactivate_();
- };
- MDCRippleFoundation.prototype.layout = function () {
- var _this = this;
- if (this.layoutFrame_) {
- cancelAnimationFrame(this.layoutFrame_);
- }
- this.layoutFrame_ = requestAnimationFrame(function () {
- _this.layoutInternal_();
- _this.layoutFrame_ = 0;
- });
- };
- MDCRippleFoundation.prototype.setUnbounded = function (unbounded) {
- var UNBOUNDED = MDCRippleFoundation.cssClasses.UNBOUNDED;
- if (unbounded) {
- this.adapter_.addClass(UNBOUNDED);
- }
- else {
- this.adapter_.removeClass(UNBOUNDED);
- }
- };
- MDCRippleFoundation.prototype.handleFocus = function () {
- var _this = this;
- requestAnimationFrame(function () {
- return _this.adapter_.addClass(MDCRippleFoundation.cssClasses.BG_FOCUSED);
- });
- };
- MDCRippleFoundation.prototype.handleBlur = function () {
- var _this = this;
- requestAnimationFrame(function () {
- return _this.adapter_.removeClass(MDCRippleFoundation.cssClasses.BG_FOCUSED);
- });
- };
- /**
- * We compute this property so that we are not querying information about the client
- * until the point in time where the foundation requests it. This prevents scenarios where
- * client-side feature-detection may happen too early, such as when components are rendered on the server
- * and then initialized at mount time on the client.
- */
- MDCRippleFoundation.prototype.supportsPressRipple_ = function () {
- return this.adapter_.browserSupportsCssVars();
- };
- MDCRippleFoundation.prototype.defaultActivationState_ = function () {
- return {
- activationEvent: undefined,
- hasDeactivationUXRun: false,
- isActivated: false,
- isProgrammatic: false,
- wasActivatedByPointer: false,
- wasElementMadeActive: false,
- };
- };
- /**
- * supportsPressRipple Passed from init to save a redundant function call
- */
- MDCRippleFoundation.prototype.registerRootHandlers_ = function (supportsPressRipple) {
- var _this = this;
- if (supportsPressRipple) {
- ACTIVATION_EVENT_TYPES.forEach(function (evtType) {
- _this.adapter_.registerInteractionHandler(evtType, _this.activateHandler_);
- });
- if (this.adapter_.isUnbounded()) {
- this.adapter_.registerResizeHandler(this.resizeHandler_);
- }
- }
- this.adapter_.registerInteractionHandler('focus', this.focusHandler_);
- this.adapter_.registerInteractionHandler('blur', this.blurHandler_);
- };
- MDCRippleFoundation.prototype.registerDeactivationHandlers_ = function (evt) {
- var _this = this;
- if (evt.type === 'keydown') {
- this.adapter_.registerInteractionHandler('keyup', this.deactivateHandler_);
- }
- else {
- POINTER_DEACTIVATION_EVENT_TYPES.forEach(function (evtType) {
- _this.adapter_.registerDocumentInteractionHandler(evtType, _this.deactivateHandler_);
- });
- }
- };
- MDCRippleFoundation.prototype.deregisterRootHandlers_ = function () {
- var _this = this;
- ACTIVATION_EVENT_TYPES.forEach(function (evtType) {
- _this.adapter_.deregisterInteractionHandler(evtType, _this.activateHandler_);
- });
- this.adapter_.deregisterInteractionHandler('focus', this.focusHandler_);
- this.adapter_.deregisterInteractionHandler('blur', this.blurHandler_);
- if (this.adapter_.isUnbounded()) {
- this.adapter_.deregisterResizeHandler(this.resizeHandler_);
- }
- };
- MDCRippleFoundation.prototype.deregisterDeactivationHandlers_ = function () {
- var _this = this;
- this.adapter_.deregisterInteractionHandler('keyup', this.deactivateHandler_);
- POINTER_DEACTIVATION_EVENT_TYPES.forEach(function (evtType) {
- _this.adapter_.deregisterDocumentInteractionHandler(evtType, _this.deactivateHandler_);
- });
- };
- MDCRippleFoundation.prototype.removeCssVars_ = function () {
- var _this = this;
- var rippleStrings = MDCRippleFoundation.strings;
- var keys = Object.keys(rippleStrings);
- keys.forEach(function (key) {
- if (key.indexOf('VAR_') === 0) {
- _this.adapter_.updateCssVariable(rippleStrings[key], null);
- }
- });
- };
- MDCRippleFoundation.prototype.activate_ = function (evt) {
- var _this = this;
- if (this.adapter_.isSurfaceDisabled()) {
- return;
- }
- var activationState = this.activationState_;
- if (activationState.isActivated) {
- return;
- }
- // Avoid reacting to follow-on events fired by touch device after an already-processed user interaction
- var previousActivationEvent = this.previousActivationEvent_;
- var isSameInteraction = previousActivationEvent && evt !== undefined && previousActivationEvent.type !== evt.type;
- if (isSameInteraction) {
- return;
- }
- activationState.isActivated = true;
- activationState.isProgrammatic = evt === undefined;
- activationState.activationEvent = evt;
- activationState.wasActivatedByPointer = activationState.isProgrammatic ? false : evt !== undefined && (evt.type === 'mousedown' || evt.type === 'touchstart' || evt.type === 'pointerdown');
- var hasActivatedChild = evt !== undefined && activatedTargets.length > 0 && activatedTargets.some(function (target) { return _this.adapter_.containsEventTarget(target); });
- if (hasActivatedChild) {
- // Immediately reset activation state, while preserving logic that prevents touch follow-on events
- this.resetActivationState_();
- return;
- }
- if (evt !== undefined) {
- activatedTargets.push(evt.target);
- this.registerDeactivationHandlers_(evt);
- }
- activationState.wasElementMadeActive = this.checkElementMadeActive_(evt);
- if (activationState.wasElementMadeActive) {
- this.animateActivation_();
- }
- requestAnimationFrame(function () {
- // Reset array on next frame after the current event has had a chance to bubble to prevent ancestor ripples
- activatedTargets = [];
- if (!activationState.wasElementMadeActive
- && evt !== undefined
- && (evt.key === ' ' || evt.keyCode === 32)) {
- // If space was pressed, try again within an rAF call to detect :active, because different UAs report
- // active states inconsistently when they're called within event handling code:
- // - https://bugs.chromium.org/p/chromium/issues/detail?id=635971
- // - https://bugzilla.mozilla.org/show_bug.cgi?id=1293741
- // We try first outside rAF to support Edge, which does not exhibit this problem, but will crash if a CSS
- // variable is set within a rAF callback for a submit button interaction (#2241).
- activationState.wasElementMadeActive = _this.checkElementMadeActive_(evt);
- if (activationState.wasElementMadeActive) {
- _this.animateActivation_();
- }
- }
- if (!activationState.wasElementMadeActive) {
- // Reset activation state immediately if element was not made active.
- _this.activationState_ = _this.defaultActivationState_();
- }
- });
- };
- MDCRippleFoundation.prototype.checkElementMadeActive_ = function (evt) {
- return (evt !== undefined && evt.type === 'keydown') ? this.adapter_.isSurfaceActive() : true;
- };
- MDCRippleFoundation.prototype.animateActivation_ = function () {
- var _this = this;
- var _a = MDCRippleFoundation.strings, VAR_FG_TRANSLATE_START = _a.VAR_FG_TRANSLATE_START, VAR_FG_TRANSLATE_END = _a.VAR_FG_TRANSLATE_END;
- var _b = MDCRippleFoundation.cssClasses, FG_DEACTIVATION = _b.FG_DEACTIVATION, FG_ACTIVATION = _b.FG_ACTIVATION;
- var DEACTIVATION_TIMEOUT_MS = MDCRippleFoundation.numbers.DEACTIVATION_TIMEOUT_MS;
- this.layoutInternal_();
- var translateStart = '';
- var translateEnd = '';
- if (!this.adapter_.isUnbounded()) {
- var _c = this.getFgTranslationCoordinates_(), startPoint = _c.startPoint, endPoint = _c.endPoint;
- translateStart = startPoint.x + "px, " + startPoint.y + "px";
- translateEnd = endPoint.x + "px, " + endPoint.y + "px";
- }
- this.adapter_.updateCssVariable(VAR_FG_TRANSLATE_START, translateStart);
- this.adapter_.updateCssVariable(VAR_FG_TRANSLATE_END, translateEnd);
- // Cancel any ongoing activation/deactivation animations
- clearTimeout(this.activationTimer_);
- clearTimeout(this.fgDeactivationRemovalTimer_);
- this.rmBoundedActivationClasses_();
- this.adapter_.removeClass(FG_DEACTIVATION);
- // Force layout in order to re-trigger the animation.
- this.adapter_.computeBoundingRect();
- this.adapter_.addClass(FG_ACTIVATION);
- this.activationTimer_ = setTimeout(function () { return _this.activationTimerCallback_(); }, DEACTIVATION_TIMEOUT_MS);
- };
- MDCRippleFoundation.prototype.getFgTranslationCoordinates_ = function () {
- var _a = this.activationState_, activationEvent = _a.activationEvent, wasActivatedByPointer = _a.wasActivatedByPointer;
- var startPoint;
- if (wasActivatedByPointer) {
- startPoint = getNormalizedEventCoords(activationEvent, this.adapter_.getWindowPageOffset(), this.adapter_.computeBoundingRect());
- }
- else {
- startPoint = {
- x: this.frame_.width / 2,
- y: this.frame_.height / 2,
- };
- }
- // Center the element around the start point.
- startPoint = {
- x: startPoint.x - (this.initialSize_ / 2),
- y: startPoint.y - (this.initialSize_ / 2),
- };
- var endPoint = {
- x: (this.frame_.width / 2) - (this.initialSize_ / 2),
- y: (this.frame_.height / 2) - (this.initialSize_ / 2),
- };
- return { startPoint: startPoint, endPoint: endPoint };
- };
- MDCRippleFoundation.prototype.runDeactivationUXLogicIfReady_ = function () {
- var _this = this;
- // This method is called both when a pointing device is released, and when the activation animation ends.
- // The deactivation animation should only run after both of those occur.
- var FG_DEACTIVATION = MDCRippleFoundation.cssClasses.FG_DEACTIVATION;
- var _a = this.activationState_, hasDeactivationUXRun = _a.hasDeactivationUXRun, isActivated = _a.isActivated;
- var activationHasEnded = hasDeactivationUXRun || !isActivated;
- if (activationHasEnded && this.activationAnimationHasEnded_) {
- this.rmBoundedActivationClasses_();
- this.adapter_.addClass(FG_DEACTIVATION);
- this.fgDeactivationRemovalTimer_ = setTimeout(function () {
- _this.adapter_.removeClass(FG_DEACTIVATION);
- }, numbers.FG_DEACTIVATION_MS);
- }
- };
- MDCRippleFoundation.prototype.rmBoundedActivationClasses_ = function () {
- var FG_ACTIVATION = MDCRippleFoundation.cssClasses.FG_ACTIVATION;
- this.adapter_.removeClass(FG_ACTIVATION);
- this.activationAnimationHasEnded_ = false;
- this.adapter_.computeBoundingRect();
- };
- MDCRippleFoundation.prototype.resetActivationState_ = function () {
- var _this = this;
- this.previousActivationEvent_ = this.activationState_.activationEvent;
- this.activationState_ = this.defaultActivationState_();
- // Touch devices may fire additional events for the same interaction within a short time.
- // Store the previous event until it's safe to assume that subsequent events are for new interactions.
- setTimeout(function () { return _this.previousActivationEvent_ = undefined; }, MDCRippleFoundation.numbers.TAP_DELAY_MS);
- };
- MDCRippleFoundation.prototype.deactivate_ = function () {
- var _this = this;
- var activationState = this.activationState_;
- // This can happen in scenarios such as when you have a keyup event that blurs the element.
- if (!activationState.isActivated) {
- return;
- }
- var state = tslib_1.__assign({}, activationState);
- if (activationState.isProgrammatic) {
- requestAnimationFrame(function () { return _this.animateDeactivation_(state); });
- this.resetActivationState_();
- }
- else {
- this.deregisterDeactivationHandlers_();
- requestAnimationFrame(function () {
- _this.activationState_.hasDeactivationUXRun = true;
- _this.animateDeactivation_(state);
- _this.resetActivationState_();
- });
- }
- };
- MDCRippleFoundation.prototype.animateDeactivation_ = function (_a) {
- var wasActivatedByPointer = _a.wasActivatedByPointer, wasElementMadeActive = _a.wasElementMadeActive;
- if (wasActivatedByPointer || wasElementMadeActive) {
- this.runDeactivationUXLogicIfReady_();
- }
- };
- MDCRippleFoundation.prototype.layoutInternal_ = function () {
- var _this = this;
- this.frame_ = this.adapter_.computeBoundingRect();
- var maxDim = Math.max(this.frame_.height, this.frame_.width);
- // Surface diameter is treated differently for unbounded vs. bounded ripples.
- // Unbounded ripple diameter is calculated smaller since the surface is expected to already be padded appropriately
- // to extend the hitbox, and the ripple is expected to meet the edges of the padded hitbox (which is typically
- // square). Bounded ripples, on the other hand, are fully expected to expand beyond the surface's longest diameter
- // (calculated based on the diagonal plus a constant padding), and are clipped at the surface's border via
- // `overflow: hidden`.
- var getBoundedRadius = function () {
- var hypotenuse = Math.sqrt(Math.pow(_this.frame_.width, 2) + Math.pow(_this.frame_.height, 2));
- return hypotenuse + MDCRippleFoundation.numbers.PADDING;
- };
- this.maxRadius_ = this.adapter_.isUnbounded() ? maxDim : getBoundedRadius();
- // Ripple is sized as a fraction of the largest dimension of the surface, then scales up using a CSS scale transform
- var initialSize = Math.floor(maxDim * MDCRippleFoundation.numbers.INITIAL_ORIGIN_SCALE);
- // Unbounded ripple size should always be even number to equally center align.
- if (this.adapter_.isUnbounded() && initialSize % 2 !== 0) {
- this.initialSize_ = initialSize - 1;
- }
- else {
- this.initialSize_ = initialSize;
- }
- this.fgScale_ = "" + this.maxRadius_ / this.initialSize_;
- this.updateLayoutCssVars_();
- };
- MDCRippleFoundation.prototype.updateLayoutCssVars_ = function () {
- var _a = MDCRippleFoundation.strings, VAR_FG_SIZE = _a.VAR_FG_SIZE, VAR_LEFT = _a.VAR_LEFT, VAR_TOP = _a.VAR_TOP, VAR_FG_SCALE = _a.VAR_FG_SCALE;
- this.adapter_.updateCssVariable(VAR_FG_SIZE, this.initialSize_ + "px");
- this.adapter_.updateCssVariable(VAR_FG_SCALE, this.fgScale_);
- if (this.adapter_.isUnbounded()) {
- this.unboundedCoords_ = {
- left: Math.round((this.frame_.width / 2) - (this.initialSize_ / 2)),
- top: Math.round((this.frame_.height / 2) - (this.initialSize_ / 2)),
- };
- this.adapter_.updateCssVariable(VAR_LEFT, this.unboundedCoords_.left + "px");
- this.adapter_.updateCssVariable(VAR_TOP, this.unboundedCoords_.top + "px");
- }
- };
- return MDCRippleFoundation;
- }(MDCFoundation));
- export { MDCRippleFoundation };
- // tslint:disable-next-line:no-default-export Needed for backward compatibility with MDC Web v0.44.0 and earlier.
- export default MDCRippleFoundation;
- //# sourceMappingURL=foundation.js.map
|