{"version":3,"file":"ripple-BT3tzh6F.mjs","sources":["../../../../../k8-fastbuild-ST-46c76129e412/bin/src/material/core/ripple/ripple-ref.ts","../../../../../k8-fastbuild-ST-46c76129e412/bin/src/material/core/ripple/ripple-event-manager.ts","../../../../../k8-fastbuild-ST-46c76129e412/bin/src/material/core/ripple/ripple-renderer.ts","../../../../../k8-fastbuild-ST-46c76129e412/bin/src/material/core/ripple/ripple.ts"],"sourcesContent":["/**\n * @license\n * Copyright Google LLC All Rights Reserved.\n *\n * Use of this source code is governed by an MIT-style license that can be\n * found in the LICENSE file at https://angular.dev/license\n */\n\n/** Possible states for a ripple element. */\nexport enum RippleState {\n FADING_IN,\n VISIBLE,\n FADING_OUT,\n HIDDEN,\n}\n\nexport type RippleConfig = {\n color?: string;\n centered?: boolean;\n radius?: number;\n persistent?: boolean;\n animation?: RippleAnimationConfig;\n terminateOnPointerUp?: boolean;\n};\n\n/**\n * Interface that describes the configuration for the animation of a ripple.\n * There are two animation phases with different durations for the ripples.\n */\nexport interface RippleAnimationConfig {\n /** Duration in milliseconds for the enter animation (expansion from point of contact). */\n enterDuration?: number;\n /** Duration in milliseconds for the exit animation (fade-out). */\n exitDuration?: number;\n}\n\n/**\n * Reference to a previously launched ripple element.\n */\nexport class RippleRef {\n /** Current state of the ripple. */\n state: RippleState = RippleState.HIDDEN;\n\n constructor(\n private _renderer: {fadeOutRipple(ref: RippleRef): void},\n /** Reference to the ripple HTML element. */\n public element: HTMLElement,\n /** Ripple configuration used for the ripple. */\n public config: RippleConfig,\n /* Whether animations are forcibly disabled for ripples through CSS. */\n public _animationForciblyDisabledThroughCss = false,\n ) {}\n\n /** Fades out the ripple element. */\n fadeOut() {\n this._renderer.fadeOutRipple(this);\n }\n}\n","/**\n * @license\n * Copyright Google LLC All Rights Reserved.\n *\n * Use of this source code is governed by an MIT-style license that can be\n * found in the LICENSE file at https://angular.dev/license\n */\n\nimport {normalizePassiveListenerOptions, _getEventTarget} from '@angular/cdk/platform';\nimport {NgZone} from '@angular/core';\n\n/** Options used to bind a passive capturing event. */\nconst passiveCapturingEventOptions = normalizePassiveListenerOptions({\n passive: true,\n capture: true,\n});\n\n/** Manages events through delegation so that as few event handlers as possible are bound. */\nexport class RippleEventManager {\n private _events = new Map>>();\n\n /** Adds an event handler. */\n addHandler(ngZone: NgZone, name: string, element: HTMLElement, handler: EventListenerObject) {\n const handlersForEvent = this._events.get(name);\n\n if (handlersForEvent) {\n const handlersForElement = handlersForEvent.get(element);\n\n if (handlersForElement) {\n handlersForElement.add(handler);\n } else {\n handlersForEvent.set(element, new Set([handler]));\n }\n } else {\n this._events.set(name, new Map([[element, new Set([handler])]]));\n\n ngZone.runOutsideAngular(() => {\n document.addEventListener(name, this._delegateEventHandler, passiveCapturingEventOptions);\n });\n }\n }\n\n /** Removes an event handler. */\n removeHandler(name: string, element: HTMLElement, handler: EventListenerObject) {\n const handlersForEvent = this._events.get(name);\n\n if (!handlersForEvent) {\n return;\n }\n\n const handlersForElement = handlersForEvent.get(element);\n\n if (!handlersForElement) {\n return;\n }\n\n handlersForElement.delete(handler);\n\n if (handlersForElement.size === 0) {\n handlersForEvent.delete(element);\n }\n\n if (handlersForEvent.size === 0) {\n this._events.delete(name);\n document.removeEventListener(name, this._delegateEventHandler, passiveCapturingEventOptions);\n }\n }\n\n /** Event handler that is bound and which dispatches the events to the different targets. */\n private _delegateEventHandler = (event: Event) => {\n const target = _getEventTarget(event);\n\n if (target) {\n this._events.get(event.type)?.forEach((handlers, element) => {\n if (element === target || element.contains(target as Node)) {\n handlers.forEach(handler => handler.handleEvent(event));\n }\n });\n }\n };\n}\n","/**\n * @license\n * Copyright Google LLC All Rights Reserved.\n *\n * Use of this source code is governed by an MIT-style license that can be\n * found in the LICENSE file at https://angular.dev/license\n */\nimport {\n ElementRef,\n NgZone,\n Component,\n ChangeDetectionStrategy,\n ViewEncapsulation,\n Injector,\n} from '@angular/core';\nimport {Platform, normalizePassiveListenerOptions, _getEventTarget} from '@angular/cdk/platform';\nimport {isFakeMousedownFromScreenReader, isFakeTouchstartFromScreenReader} from '@angular/cdk/a11y';\nimport {coerceElement} from '@angular/cdk/coercion';\nimport {_CdkPrivateStyleLoader} from '@angular/cdk/private';\nimport {RippleRef, RippleState, RippleConfig} from './ripple-ref';\nimport {RippleEventManager} from './ripple-event-manager';\n\n/**\n * Interface that describes the target for launching ripples.\n * It defines the ripple configuration and disabled state for interaction ripples.\n * @docs-private\n */\nexport interface RippleTarget {\n /** Configuration for ripples that are launched on pointer down. */\n rippleConfig: RippleConfig;\n /** Whether ripples on pointer down should be disabled. */\n rippleDisabled: boolean;\n}\n\n/** Interfaces the defines ripple element transition event listeners. */\ninterface RippleEventListeners {\n onTransitionEnd: EventListener;\n onTransitionCancel: EventListener;\n fallbackTimer: ReturnType | null;\n}\n\n/**\n * Default ripple animation configuration for ripples without an explicit\n * animation config specified.\n */\nexport const defaultRippleAnimationConfig = {\n enterDuration: 225,\n exitDuration: 150,\n};\n\n/**\n * Timeout for ignoring mouse events. Mouse events will be temporary ignored after touch\n * events to avoid synthetic mouse events.\n */\nconst ignoreMouseEventsTimeout = 800;\n\n/** Options used to bind a passive capturing event. */\nconst passiveCapturingEventOptions = normalizePassiveListenerOptions({\n passive: true,\n capture: true,\n});\n\n/** Events that signal that the pointer is down. */\nconst pointerDownEvents = ['mousedown', 'touchstart'];\n\n/** Events that signal that the pointer is up. */\nconst pointerUpEvents = ['mouseup', 'mouseleave', 'touchend', 'touchcancel'];\n\n@Component({\n template: '',\n changeDetection: ChangeDetectionStrategy.OnPush,\n encapsulation: ViewEncapsulation.None,\n styleUrl: 'ripple-structure.css',\n host: {'mat-ripple-style-loader': ''},\n})\nexport class _MatRippleStylesLoader {}\n\n/**\n * Helper service that performs DOM manipulations. Not intended to be used outside this module.\n * The constructor takes a reference to the ripple directive's host element and a map of DOM\n * event handlers to be installed on the element that triggers ripple animations.\n * This will eventually become a custom renderer once Angular support exists.\n * @docs-private\n */\nexport class RippleRenderer implements EventListenerObject {\n /** Element where the ripples are being added to. */\n private _containerElement: HTMLElement;\n\n /** Element which triggers the ripple elements on mouse events. */\n private _triggerElement: HTMLElement | null;\n\n /** Whether the pointer is currently down or not. */\n private _isPointerDown = false;\n\n /**\n * Map of currently active ripple references.\n * The ripple reference is mapped to its element event listeners.\n * The reason why `| null` is used is that event listeners are added only\n * when the condition is truthy (see the `_startFadeOutTransition` method).\n */\n private _activeRipples = new Map();\n\n /** Latest non-persistent ripple that was triggered. */\n private _mostRecentTransientRipple: RippleRef | null;\n\n /** Time in milliseconds when the last touchstart event happened. */\n private _lastTouchStartEvent: number;\n\n /** Whether pointer-up event listeners have been registered. */\n private _pointerUpEventsRegistered = false;\n\n /**\n * Cached dimensions of the ripple container. Set when the first\n * ripple is shown and cleared once no more ripples are visible.\n */\n private _containerRect: DOMRect | null;\n\n private static _eventManager = new RippleEventManager();\n\n constructor(\n private _target: RippleTarget,\n private _ngZone: NgZone,\n elementOrElementRef: HTMLElement | ElementRef,\n private _platform: Platform,\n injector?: Injector,\n ) {\n // Only do anything if we're on the browser.\n if (_platform.isBrowser) {\n this._containerElement = coerceElement(elementOrElementRef);\n }\n\n if (injector) {\n injector.get(_CdkPrivateStyleLoader).load(_MatRippleStylesLoader);\n }\n }\n\n /**\n * Fades in a ripple at the given coordinates.\n * @param x Coordinate within the element, along the X axis at which to start the ripple.\n * @param y Coordinate within the element, along the Y axis at which to start the ripple.\n * @param config Extra ripple options.\n */\n fadeInRipple(x: number, y: number, config: RippleConfig = {}): RippleRef {\n const containerRect = (this._containerRect =\n this._containerRect || this._containerElement.getBoundingClientRect());\n const animationConfig = {...defaultRippleAnimationConfig, ...config.animation};\n\n if (config.centered) {\n x = containerRect.left + containerRect.width / 2;\n y = containerRect.top + containerRect.height / 2;\n }\n\n const radius = config.radius || distanceToFurthestCorner(x, y, containerRect);\n const offsetX = x - containerRect.left;\n const offsetY = y - containerRect.top;\n const enterDuration = animationConfig.enterDuration;\n\n const ripple = document.createElement('div');\n ripple.classList.add('mat-ripple-element');\n\n ripple.style.left = `${offsetX - radius}px`;\n ripple.style.top = `${offsetY - radius}px`;\n ripple.style.height = `${radius * 2}px`;\n ripple.style.width = `${radius * 2}px`;\n\n // If a custom color has been specified, set it as inline style. If no color is\n // set, the default color will be applied through the ripple theme styles.\n if (config.color != null) {\n ripple.style.backgroundColor = config.color;\n }\n\n ripple.style.transitionDuration = `${enterDuration}ms`;\n\n this._containerElement.appendChild(ripple);\n\n // By default the browser does not recalculate the styles of dynamically created\n // ripple elements. This is critical to ensure that the `scale` animates properly.\n // We enforce a style recalculation by calling `getComputedStyle` and *accessing* a property.\n // See: https://gist.github.com/paulirish/5d52fb081b3570c81e3a\n const computedStyles = window.getComputedStyle(ripple);\n const userTransitionProperty = computedStyles.transitionProperty;\n const userTransitionDuration = computedStyles.transitionDuration;\n\n // Note: We detect whether animation is forcibly disabled through CSS (e.g. through\n // `transition: none` or `display: none`). This is technically unexpected since animations are\n // controlled through the animation config, but this exists for backwards compatibility. This\n // logic does not need to be super accurate since it covers some edge cases which can be easily\n // avoided by users.\n const animationForciblyDisabledThroughCss =\n userTransitionProperty === 'none' ||\n // Note: The canonical unit for serialized CSS `