UNPKG

@angular/material

Version:
1 lines 132 kB
{"version":3,"file":"slider.mjs","sources":["../../../../../k8-fastbuild-ST-46c76129e412/bin/src/material/slider/slider-interface.ts","../../../../../k8-fastbuild-ST-46c76129e412/bin/src/material/slider/slider-thumb.ts","../../../../../k8-fastbuild-ST-46c76129e412/bin/src/material/slider/slider-thumb.html","../../../../../k8-fastbuild-ST-46c76129e412/bin/src/material/slider/slider.ts","../../../../../k8-fastbuild-ST-46c76129e412/bin/src/material/slider/slider.html","../../../../../k8-fastbuild-ST-46c76129e412/bin/src/material/slider/slider-input.ts","../../../../../k8-fastbuild-ST-46c76129e412/bin/src/material/slider/module.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\nimport {InjectionToken, ChangeDetectorRef, WritableSignal} from '@angular/core';\nimport {MatRipple, RippleGlobalOptions} from '../core';\n\n/**\n * Thumb types: range slider has two thumbs (START, END) whereas single point\n * slider only has one thumb (END).\n */\nexport enum _MatThumb {\n START = 1,\n END = 2,\n}\n\n/** Tick mark enum, for discrete sliders. */\nexport enum _MatTickMark {\n ACTIVE = 0,\n INACTIVE = 1,\n}\n\n/**\n * Injection token that can be used for a `MatSlider` to provide itself as a\n * parent to the `MatSliderThumb` and `MatSliderRangeThumb`.\n * Used primarily to avoid circular imports.\n * @docs-private\n */\nexport const MAT_SLIDER = new InjectionToken<{}>('_MatSlider');\n\n/**\n * Injection token that can be used to query for a `MatSliderThumb`.\n * Used primarily to avoid circular imports.\n * @docs-private\n */\nexport const MAT_SLIDER_THUMB = new InjectionToken<{}>('_MatSliderThumb');\n\n/**\n * Injection token that can be used to query for a `MatSliderRangeThumb`.\n * Used primarily to avoid circular imports.\n * @docs-private\n */\nexport const MAT_SLIDER_RANGE_THUMB = new InjectionToken<{}>('_MatSliderRangeThumb');\n\n/**\n * Injection token that can be used to query for a `MatSliderVisualThumb`.\n * Used primarily to avoid circular imports.\n * @docs-private\n */\nexport const MAT_SLIDER_VISUAL_THUMB = new InjectionToken<{}>('_MatSliderVisualThumb');\n\n/** Represents a drag event emitted by the MatSlider component. */\nexport interface MatSliderDragEvent {\n /** The MatSliderThumb that was interacted with. */\n source: _MatSliderThumb;\n\n /** The MatSlider that was interacted with. */\n parent: _MatSlider;\n\n /** The current value of the slider. */\n value: number;\n}\n\n/**\n * A simple change event emitted by the MatSlider component.\n * @deprecated Use event bindings directly on the MatSliderThumbs for `change` and `input` events. See https://v17.material.angular.io/guide/mdc-migration for information about migrating.\n * @breaking-change 17.0.0\n */\nexport class MatSliderChange {\n /** The MatSliderThumb that was interacted with. */\n source: _MatSliderThumb;\n\n /** The MatSlider that was interacted with. */\n parent: _MatSlider;\n\n /** The new value of the source slider. */\n value: number;\n}\n\nexport interface _MatSlider {\n /** Whether the given pointer event occurred within the bounds of the slider pointer's DOM Rect. */\n _isCursorOnSliderThumb(event: PointerEvent, rect: DOMRect): boolean;\n\n /** Gets the slider thumb input of the given thumb position. */\n _getInput(thumbPosition: _MatThumb): _MatSliderThumb | _MatSliderRangeThumb | undefined;\n\n /** Gets the slider thumb HTML input element of the given thumb position. */\n _getThumb(thumbPosition: _MatThumb): _MatSliderVisualThumb;\n\n /** The minimum value that the slider can have. */\n min: number;\n\n /** The maximum value that the slider can have. */\n max: number;\n\n /** The amount that slider values can increment or decrement by. */\n step: number;\n\n /** Whether the slider is disabled. */\n disabled: boolean;\n\n /** Whether the slider is a range slider. */\n _isRange: boolean;\n\n /** Whether the slider is rtl. */\n _isRtl: boolean;\n\n /** The stored width of the host element's bounding client rect. */\n _cachedWidth: number;\n\n /** The stored width of the host element's bounding client rect. */\n _cachedLeft: number;\n\n /**\n * The padding of the native slider input. This is added in order to make the region where the\n * thumb ripple extends past the end of the slider track clickable.\n */\n _inputPadding: number;\n\n /** The radius of the visual slider's ripple. */\n _rippleRadius: number;\n\n /** The global configuration for `matRipple` instances. */\n readonly _globalRippleOptions: RippleGlobalOptions | null;\n\n /** Whether animations have been disabled. */\n _noopAnimations: boolean;\n\n /** Whether or not the slider should use animations. */\n _hasAnimation: boolean;\n\n /** Triggers UI updates that are needed after a slider input value has changed. */\n _onValueChange: (source: _MatSliderThumb) => void;\n\n /** Triggers UI updates that are needed after the slider thumb position has changed. */\n _onTranslateXChange: (source: _MatSliderThumb) => void;\n\n /** Updates the stored slider dimensions using the current bounding client rect. */\n _updateDimensions: () => void;\n\n /** Updates the scale on the active portion of the track. */\n _updateTrackUI: (source: _MatSliderThumb) => void;\n\n /** Used to set the transition duration for thumb and track animations. */\n _setTransition: (withAnimation: boolean) => void;\n\n _cdr: ChangeDetectorRef;\n}\n\nexport interface _MatSliderThumb {\n /** The minimum value that the slider can have. */\n min: number;\n\n /** The maximum value that the slider can have. */\n max: number;\n\n /** The amount that slider values can increment or decrement by. */\n step: number;\n\n /** The current value of this slider input. */\n value: number;\n\n /** The current translateX in px of the slider visual thumb. */\n translateX: number;\n\n /** Indicates whether this thumb is the start or end thumb. */\n thumbPosition: _MatThumb;\n\n /** Similar to percentage but calcualted using translateX relative to the total track width. */\n fillPercentage: number;\n\n /** Whether the slider is disabled. */\n disabled: boolean;\n\n /** The host native HTML input element. */\n _hostElement: HTMLInputElement;\n\n /** Whether the input is currently focused (either by tab or after clicking). */\n _isFocused: boolean;\n\n /** The aria-valuetext string representation of the input's value. */\n _valuetext: WritableSignal<string>;\n\n /**\n * Indicates whether UI updates should be skipped.\n *\n * This flag is used to avoid flickering\n * when correcting values on pointer up/down.\n */\n _skipUIUpdate: boolean;\n\n /** Handles the initialization of properties for the slider input. */\n initProps: () => void;\n\n /** Handles UI initialization controlled by this slider input. */\n initUI: () => void;\n\n /** Calculates the visual thumb's translateX based on the slider input's current value. */\n _calcTranslateXByValue: () => number;\n\n /** Updates the visual thumb based on the slider input's current value. */\n _updateThumbUIByValue: () => void;\n\n /**\n * Sets the slider input to disproportionate dimensions to allow for touch\n * events to be captured on touch devices.\n */\n _updateWidthInactive: () => void;\n\n /**\n * Used to set the slider width to the correct\n * dimensions while the user is dragging.\n */\n _updateWidthActive: () => void;\n}\n\nexport interface _MatSliderRangeThumb extends _MatSliderThumb {\n /** Whether this slider corresponds to the input on the left hand side. */\n _isLeftThumb: boolean;\n\n /**\n * Gets the sibling MatSliderRangeThumb.\n * Returns undefined if it is too early in Angular's life cycle.\n */\n getSibling: () => _MatSliderRangeThumb | undefined;\n\n /** Used to cache whether this slider input corresponds to the visual left thumb. */\n _setIsLeftThumb: () => void;\n\n /** Updates the input styles to control whether it is pinned to the start or end of the mat-slider. */\n _updateStaticStyles: () => void;\n\n /** Updates the min and max properties of this slider input according to it's sibling. */\n _updateMinMax: () => void;\n}\n\nexport interface _MatSliderVisualThumb {\n /** The MatRipple for this slider thumb. */\n _ripple: MatRipple;\n\n /** Whether the slider thumb is currently being pressed. */\n _isActive: boolean;\n\n /** The host native HTML input element. */\n _hostElement: HTMLElement;\n\n /** Shows the value indicator ui. */\n _showValueIndicator: () => void;\n\n /** Hides the value indicator ui. */\n _hideValueIndicator: () => void;\n\n /** Whether the slider visual thumb is currently showing any ripple. */\n _isShowingAnyRipple: () => boolean;\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 {\n AfterViewInit,\n ChangeDetectionStrategy,\n ChangeDetectorRef,\n Component,\n ElementRef,\n Input,\n NgZone,\n OnDestroy,\n Renderer2,\n ViewChild,\n ViewEncapsulation,\n inject,\n} from '@angular/core';\nimport {MatRipple, RippleAnimationConfig, RippleRef, RippleState} from '../core';\nimport {\n _MatThumb,\n _MatSlider,\n _MatSliderThumb,\n _MatSliderVisualThumb,\n MAT_SLIDER,\n MAT_SLIDER_VISUAL_THUMB,\n} from './slider-interface';\nimport {Platform} from '@angular/cdk/platform';\n\n/**\n * The visual slider thumb.\n *\n * Handles the slider thumb ripple states (hover, focus, and active),\n * and displaying the value tooltip on discrete sliders.\n * @docs-private\n */\n@Component({\n selector: 'mat-slider-visual-thumb',\n templateUrl: './slider-thumb.html',\n styleUrl: 'slider-thumb.css',\n host: {\n 'class': 'mdc-slider__thumb mat-mdc-slider-visual-thumb',\n },\n changeDetection: ChangeDetectionStrategy.OnPush,\n encapsulation: ViewEncapsulation.None,\n providers: [{provide: MAT_SLIDER_VISUAL_THUMB, useExisting: MatSliderVisualThumb}],\n imports: [MatRipple],\n})\nexport class MatSliderVisualThumb implements _MatSliderVisualThumb, AfterViewInit, OnDestroy {\n readonly _cdr = inject(ChangeDetectorRef);\n private readonly _ngZone = inject(NgZone);\n private _slider = inject<_MatSlider>(MAT_SLIDER);\n private _renderer = inject(Renderer2);\n private _listenerCleanups: (() => void)[] | undefined;\n\n /** Whether the slider displays a numeric value label upon pressing the thumb. */\n @Input() discrete: boolean;\n\n /** Indicates which slider thumb this input corresponds to. */\n @Input() thumbPosition: _MatThumb;\n\n /** The display value of the slider thumb. */\n @Input() valueIndicatorText: string;\n\n /** The MatRipple for this slider thumb. */\n @ViewChild(MatRipple) readonly _ripple: MatRipple;\n\n /** The slider thumb knob. */\n @ViewChild('knob') _knob: ElementRef<HTMLElement>;\n\n /** The slider thumb value indicator container. */\n @ViewChild('valueIndicatorContainer')\n _valueIndicatorContainer: ElementRef<HTMLElement>;\n\n /** The slider input corresponding to this slider thumb. */\n private _sliderInput: _MatSliderThumb;\n\n /** The native html element of the slider input corresponding to this thumb. */\n private _sliderInputEl: HTMLInputElement | undefined;\n\n /** The RippleRef for the slider thumbs hover state. */\n private _hoverRippleRef: RippleRef | undefined;\n\n /** The RippleRef for the slider thumbs focus state. */\n private _focusRippleRef: RippleRef | undefined;\n\n /** The RippleRef for the slider thumbs active state. */\n private _activeRippleRef: RippleRef | undefined;\n\n /** Whether the slider thumb is currently being hovered. */\n private _isHovered: boolean = false;\n\n /** Whether the slider thumb is currently being pressed. */\n _isActive = false;\n\n /** Whether the value indicator tooltip is visible. */\n _isValueIndicatorVisible: boolean = false;\n\n /** The host native HTML input element. */\n _hostElement = inject<ElementRef<HTMLElement>>(ElementRef).nativeElement;\n\n private _platform = inject(Platform);\n\n constructor(...args: unknown[]);\n constructor() {}\n\n ngAfterViewInit() {\n const sliderInput = this._slider._getInput(this.thumbPosition);\n\n // No-op if the slider isn't configured properly. `MatSlider` will\n // throw an error instructing the user how to set up the slider.\n if (!sliderInput) {\n return;\n }\n\n this._ripple.radius = 24;\n this._sliderInput = sliderInput;\n this._sliderInputEl = this._sliderInput._hostElement;\n\n // These listeners don't update any data bindings so we bind them outside\n // of the NgZone to prevent Angular from needlessly running change detection.\n this._ngZone.runOutsideAngular(() => {\n const input = this._sliderInputEl!;\n const renderer = this._renderer;\n this._listenerCleanups = [\n renderer.listen(input, 'pointermove', this._onPointerMove),\n renderer.listen(input, 'pointerdown', this._onDragStart),\n renderer.listen(input, 'pointerup', this._onDragEnd),\n renderer.listen(input, 'pointerleave', this._onMouseLeave),\n renderer.listen(input, 'focus', this._onFocus),\n renderer.listen(input, 'blur', this._onBlur),\n ];\n });\n }\n\n ngOnDestroy() {\n this._listenerCleanups?.forEach(cleanup => cleanup());\n }\n\n private _onPointerMove = (event: PointerEvent): void => {\n if (this._sliderInput._isFocused) {\n return;\n }\n\n const rect = this._hostElement.getBoundingClientRect();\n const isHovered = this._slider._isCursorOnSliderThumb(event, rect);\n this._isHovered = isHovered;\n\n if (isHovered) {\n this._showHoverRipple();\n } else {\n this._hideRipple(this._hoverRippleRef);\n }\n };\n\n private _onMouseLeave = (): void => {\n this._isHovered = false;\n this._hideRipple(this._hoverRippleRef);\n };\n\n private _onFocus = (): void => {\n // We don't want to show the hover ripple on top of the focus ripple.\n // Happen when the users cursor is over a thumb and then the user tabs to it.\n this._hideRipple(this._hoverRippleRef);\n this._showFocusRipple();\n this._hostElement.classList.add('mdc-slider__thumb--focused');\n };\n\n private _onBlur = (): void => {\n // Happens when the user tabs away while still dragging a thumb.\n if (!this._isActive) {\n this._hideRipple(this._focusRippleRef);\n }\n // Happens when the user tabs away from a thumb but their cursor is still over it.\n if (this._isHovered) {\n this._showHoverRipple();\n }\n this._hostElement.classList.remove('mdc-slider__thumb--focused');\n };\n\n private _onDragStart = (event: PointerEvent): void => {\n if (event.button !== 0) {\n return;\n }\n this._isActive = true;\n this._showActiveRipple();\n };\n\n private _onDragEnd = (): void => {\n this._isActive = false;\n this._hideRipple(this._activeRippleRef);\n // Happens when the user starts dragging a thumb, tabs away, and then stops dragging.\n if (!this._sliderInput._isFocused) {\n this._hideRipple(this._focusRippleRef);\n }\n\n // On Safari we need to immediately re-show the hover ripple because\n // sliders do not retain focus from pointer events on that platform.\n if (this._platform.SAFARI) {\n this._showHoverRipple();\n }\n };\n\n /** Handles displaying the hover ripple. */\n private _showHoverRipple(): void {\n if (!this._isShowingRipple(this._hoverRippleRef)) {\n this._hoverRippleRef = this._showRipple({enterDuration: 0, exitDuration: 0});\n this._hoverRippleRef?.element.classList.add('mat-mdc-slider-hover-ripple');\n }\n }\n\n /** Handles displaying the focus ripple. */\n private _showFocusRipple(): void {\n // Show the focus ripple event if noop animations are enabled.\n if (!this._isShowingRipple(this._focusRippleRef)) {\n this._focusRippleRef = this._showRipple({enterDuration: 0, exitDuration: 0}, true);\n this._focusRippleRef?.element.classList.add('mat-mdc-slider-focus-ripple');\n }\n }\n\n /** Handles displaying the active ripple. */\n private _showActiveRipple(): void {\n if (!this._isShowingRipple(this._activeRippleRef)) {\n this._activeRippleRef = this._showRipple({enterDuration: 225, exitDuration: 400});\n this._activeRippleRef?.element.classList.add('mat-mdc-slider-active-ripple');\n }\n }\n\n /** Whether the given rippleRef is currently fading in or visible. */\n private _isShowingRipple(rippleRef?: RippleRef): boolean {\n return rippleRef?.state === RippleState.FADING_IN || rippleRef?.state === RippleState.VISIBLE;\n }\n\n /** Manually launches the slider thumb ripple using the specified ripple animation config. */\n private _showRipple(\n animation: RippleAnimationConfig,\n ignoreGlobalRippleConfig?: boolean,\n ): RippleRef | undefined {\n if (this._slider.disabled) {\n return;\n }\n this._showValueIndicator();\n if (this._slider._isRange) {\n const sibling = this._slider._getThumb(\n this.thumbPosition === _MatThumb.START ? _MatThumb.END : _MatThumb.START,\n );\n sibling._showValueIndicator();\n }\n if (this._slider._globalRippleOptions?.disabled && !ignoreGlobalRippleConfig) {\n return;\n }\n return this._ripple.launch({\n animation: this._slider._noopAnimations ? {enterDuration: 0, exitDuration: 0} : animation,\n centered: true,\n persistent: true,\n });\n }\n\n /**\n * Fades out the given ripple.\n * Also hides the value indicator if no ripple is showing.\n */\n private _hideRipple(rippleRef?: RippleRef): void {\n rippleRef?.fadeOut();\n\n if (this._isShowingAnyRipple()) {\n return;\n }\n\n if (!this._slider._isRange) {\n this._hideValueIndicator();\n }\n\n const sibling = this._getSibling();\n if (!sibling._isShowingAnyRipple()) {\n this._hideValueIndicator();\n sibling._hideValueIndicator();\n }\n }\n\n /** Shows the value indicator ui. */\n _showValueIndicator(): void {\n this._hostElement.classList.add('mdc-slider__thumb--with-indicator');\n }\n\n /** Hides the value indicator ui. */\n _hideValueIndicator(): void {\n this._hostElement.classList.remove('mdc-slider__thumb--with-indicator');\n }\n\n _getSibling(): _MatSliderVisualThumb {\n return this._slider._getThumb(\n this.thumbPosition === _MatThumb.START ? _MatThumb.END : _MatThumb.START,\n );\n }\n\n /** Gets the value indicator container's native HTML element. */\n _getValueIndicatorContainer(): HTMLElement | undefined {\n return this._valueIndicatorContainer?.nativeElement;\n }\n\n /** Gets the native HTML element of the slider thumb knob. */\n _getKnob(): HTMLElement {\n return this._knob.nativeElement;\n }\n\n _isShowingAnyRipple(): boolean {\n return (\n this._isShowingRipple(this._hoverRippleRef) ||\n this._isShowingRipple(this._focusRippleRef) ||\n this._isShowingRipple(this._activeRippleRef)\n );\n }\n}\n","@if (discrete) {\n <div class=\"mdc-slider__value-indicator-container\" #valueIndicatorContainer>\n <div class=\"mdc-slider__value-indicator\">\n <span class=\"mdc-slider__value-indicator-text\">{{valueIndicatorText}}</span>\n </div>\n </div>\n}\n<div class=\"mdc-slider__thumb-knob\" #knob></div>\n<div matRipple class=\"mat-focus-indicator\" [matRippleDisabled]=\"true\"></div>\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 {Directionality} from '@angular/cdk/bidi';\nimport {Platform} from '@angular/cdk/platform';\nimport {\n AfterViewInit,\n booleanAttribute,\n ChangeDetectionStrategy,\n ChangeDetectorRef,\n Component,\n ContentChild,\n ContentChildren,\n ElementRef,\n inject,\n Input,\n NgZone,\n numberAttribute,\n OnDestroy,\n QueryList,\n ViewChild,\n ViewChildren,\n ViewEncapsulation,\n ANIMATION_MODULE_TYPE,\n} from '@angular/core';\nimport {\n _StructuralStylesLoader,\n MAT_RIPPLE_GLOBAL_OPTIONS,\n RippleGlobalOptions,\n ThemePalette,\n} from '../core';\nimport {Subscription} from 'rxjs';\nimport {\n _MatThumb,\n _MatTickMark,\n _MatSlider,\n _MatSliderRangeThumb,\n _MatSliderThumb,\n _MatSliderVisualThumb,\n MAT_SLIDER_RANGE_THUMB,\n MAT_SLIDER_THUMB,\n MAT_SLIDER,\n MAT_SLIDER_VISUAL_THUMB,\n} from './slider-interface';\nimport {MatSliderVisualThumb} from './slider-thumb';\nimport {_CdkPrivateStyleLoader} from '@angular/cdk/private';\n\n// TODO(wagnermaciel): maybe handle the following edge case:\n// 1. start dragging discrete slider\n// 2. tab to disable checkbox\n// 3. without ending drag, disable the slider\n\n/**\n * Allows users to select from a range of values by moving the slider thumb. It is similar in\n * behavior to the native `<input type=\"range\">` element.\n */\n@Component({\n selector: 'mat-slider',\n templateUrl: 'slider.html',\n styleUrl: 'slider.css',\n host: {\n 'class': 'mat-mdc-slider mdc-slider',\n '[class]': '\"mat-\" + (color || \"primary\")',\n '[class.mdc-slider--range]': '_isRange',\n '[class.mdc-slider--disabled]': 'disabled',\n '[class.mdc-slider--discrete]': 'discrete',\n '[class.mdc-slider--tick-marks]': 'showTickMarks',\n '[class._mat-animation-noopable]': '_noopAnimations',\n },\n exportAs: 'matSlider',\n changeDetection: ChangeDetectionStrategy.OnPush,\n encapsulation: ViewEncapsulation.None,\n providers: [{provide: MAT_SLIDER, useExisting: MatSlider}],\n imports: [MatSliderVisualThumb],\n})\nexport class MatSlider implements AfterViewInit, OnDestroy, _MatSlider {\n readonly _ngZone = inject(NgZone);\n readonly _cdr = inject(ChangeDetectorRef);\n readonly _elementRef = inject<ElementRef<HTMLElement>>(ElementRef);\n readonly _dir = inject(Directionality, {optional: true});\n readonly _globalRippleOptions = inject<RippleGlobalOptions>(MAT_RIPPLE_GLOBAL_OPTIONS, {\n optional: true,\n });\n\n /** The active portion of the slider track. */\n @ViewChild('trackActive') _trackActive: ElementRef<HTMLElement>;\n\n /** The slider thumb(s). */\n @ViewChildren(MAT_SLIDER_VISUAL_THUMB) _thumbs: QueryList<_MatSliderVisualThumb>;\n\n /** The sliders hidden range input(s). */\n @ContentChild(MAT_SLIDER_THUMB) _input: _MatSliderThumb;\n\n /** The sliders hidden range input(s). */\n @ContentChildren(MAT_SLIDER_RANGE_THUMB, {descendants: false})\n _inputs: QueryList<_MatSliderRangeThumb>;\n\n /** Whether the slider is disabled. */\n @Input({transform: booleanAttribute})\n get disabled(): boolean {\n return this._disabled;\n }\n set disabled(v: boolean) {\n this._disabled = v;\n const endInput = this._getInput(_MatThumb.END);\n const startInput = this._getInput(_MatThumb.START);\n\n if (endInput) {\n endInput.disabled = this._disabled;\n }\n if (startInput) {\n startInput.disabled = this._disabled;\n }\n }\n private _disabled: boolean = false;\n\n /** Whether the slider displays a numeric value label upon pressing the thumb. */\n @Input({transform: booleanAttribute})\n get discrete(): boolean {\n return this._discrete;\n }\n set discrete(v: boolean) {\n this._discrete = v;\n this._updateValueIndicatorUIs();\n }\n private _discrete: boolean = false;\n\n /** Whether the slider displays tick marks along the slider track. */\n @Input({transform: booleanAttribute})\n showTickMarks: boolean = false;\n\n /** The minimum value that the slider can have. */\n @Input({transform: numberAttribute})\n get min(): number {\n return this._min;\n }\n set min(v: number) {\n const min = isNaN(v) ? this._min : v;\n if (this._min !== min) {\n this._updateMin(min);\n }\n }\n private _min: number = 0;\n\n /**\n * Theme color of the slider. This API is supported in M2 themes only, it\n * has no effect in M3 themes. For color customization in M3, see https://material.angular.io/components/slider/styling.\n *\n * For information on applying color variants in M3, see\n * https://material.angular.io/guide/material-2-theming#optional-add-backwards-compatibility-styles-for-color-variants\n */\n @Input()\n color: ThemePalette;\n\n /** Whether ripples are disabled in the slider. */\n @Input({transform: booleanAttribute})\n disableRipple: boolean = false;\n\n private _updateMin(min: number): void {\n const prevMin = this._min;\n this._min = min;\n this._isRange ? this._updateMinRange({old: prevMin, new: min}) : this._updateMinNonRange(min);\n this._onMinMaxOrStepChange();\n }\n\n private _updateMinRange(min: {old: number; new: number}): void {\n const endInput = this._getInput(_MatThumb.END) as _MatSliderRangeThumb;\n const startInput = this._getInput(_MatThumb.START) as _MatSliderRangeThumb;\n\n const oldEndValue = endInput.value;\n const oldStartValue = startInput.value;\n\n startInput.min = min.new;\n endInput.min = Math.max(min.new, startInput.value);\n startInput.max = Math.min(endInput.max, endInput.value);\n\n startInput._updateWidthInactive();\n endInput._updateWidthInactive();\n\n min.new < min.old\n ? this._onTranslateXChangeBySideEffect(endInput, startInput)\n : this._onTranslateXChangeBySideEffect(startInput, endInput);\n\n if (oldEndValue !== endInput.value) {\n this._onValueChange(endInput);\n }\n\n if (oldStartValue !== startInput.value) {\n this._onValueChange(startInput);\n }\n }\n\n private _updateMinNonRange(min: number): void {\n const input = this._getInput(_MatThumb.END);\n if (input) {\n const oldValue = input.value;\n\n input.min = min;\n input._updateThumbUIByValue();\n this._updateTrackUI(input);\n\n if (oldValue !== input.value) {\n this._onValueChange(input);\n }\n }\n }\n\n /** The maximum value that the slider can have. */\n @Input({transform: numberAttribute})\n get max(): number {\n return this._max;\n }\n set max(v: number) {\n const max = isNaN(v) ? this._max : v;\n if (this._max !== max) {\n this._updateMax(max);\n }\n }\n private _max: number = 100;\n\n private _updateMax(max: number): void {\n const prevMax = this._max;\n this._max = max;\n this._isRange ? this._updateMaxRange({old: prevMax, new: max}) : this._updateMaxNonRange(max);\n this._onMinMaxOrStepChange();\n }\n\n private _updateMaxRange(max: {old: number; new: number}): void {\n const endInput = this._getInput(_MatThumb.END) as _MatSliderRangeThumb;\n const startInput = this._getInput(_MatThumb.START) as _MatSliderRangeThumb;\n\n const oldEndValue = endInput.value;\n const oldStartValue = startInput.value;\n\n endInput.max = max.new;\n startInput.max = Math.min(max.new, endInput.value);\n endInput.min = startInput.value;\n\n endInput._updateWidthInactive();\n startInput._updateWidthInactive();\n\n max.new > max.old\n ? this._onTranslateXChangeBySideEffect(startInput, endInput)\n : this._onTranslateXChangeBySideEffect(endInput, startInput);\n\n if (oldEndValue !== endInput.value) {\n this._onValueChange(endInput);\n }\n\n if (oldStartValue !== startInput.value) {\n this._onValueChange(startInput);\n }\n }\n\n private _updateMaxNonRange(max: number): void {\n const input = this._getInput(_MatThumb.END);\n if (input) {\n const oldValue = input.value;\n\n input.max = max;\n input._updateThumbUIByValue();\n this._updateTrackUI(input);\n\n if (oldValue !== input.value) {\n this._onValueChange(input);\n }\n }\n }\n\n /** The values at which the thumb will snap. */\n @Input({transform: numberAttribute})\n get step(): number {\n return this._step;\n }\n set step(v: number) {\n const step = isNaN(v) ? this._step : v;\n if (this._step !== step) {\n this._updateStep(step);\n }\n }\n private _step: number = 1;\n\n private _updateStep(step: number): void {\n this._step = step;\n this._isRange ? this._updateStepRange() : this._updateStepNonRange();\n this._onMinMaxOrStepChange();\n }\n\n private _updateStepRange(): void {\n const endInput = this._getInput(_MatThumb.END) as _MatSliderRangeThumb;\n const startInput = this._getInput(_MatThumb.START) as _MatSliderRangeThumb;\n\n const oldEndValue = endInput.value;\n const oldStartValue = startInput.value;\n\n const prevStartValue = startInput.value;\n\n endInput.min = this._min;\n startInput.max = this._max;\n\n endInput.step = this._step;\n startInput.step = this._step;\n\n if (this._platform.SAFARI) {\n endInput.value = endInput.value;\n startInput.value = startInput.value;\n }\n\n endInput.min = Math.max(this._min, startInput.value);\n startInput.max = Math.min(this._max, endInput.value);\n\n startInput._updateWidthInactive();\n endInput._updateWidthInactive();\n\n endInput.value < prevStartValue\n ? this._onTranslateXChangeBySideEffect(startInput, endInput)\n : this._onTranslateXChangeBySideEffect(endInput, startInput);\n\n if (oldEndValue !== endInput.value) {\n this._onValueChange(endInput);\n }\n\n if (oldStartValue !== startInput.value) {\n this._onValueChange(startInput);\n }\n }\n\n private _updateStepNonRange(): void {\n const input = this._getInput(_MatThumb.END);\n if (input) {\n const oldValue = input.value;\n\n input.step = this._step;\n if (this._platform.SAFARI) {\n input.value = input.value;\n }\n\n input._updateThumbUIByValue();\n\n if (oldValue !== input.value) {\n this._onValueChange(input);\n }\n }\n }\n\n /**\n * Function that will be used to format the value before it is displayed\n * in the thumb label. Can be used to format very large number in order\n * for them to fit into the slider thumb.\n */\n @Input() displayWith: (value: number) => string = (value: number) => `${value}`;\n\n /** Used to keep track of & render the active & inactive tick marks on the slider track. */\n _tickMarks: _MatTickMark[];\n\n /** Whether animations have been disabled. */\n _noopAnimations: boolean;\n\n /** Subscription to changes to the directionality (LTR / RTL) context for the application. */\n private _dirChangeSubscription: Subscription;\n\n /** Observer used to monitor size changes in the slider. */\n private _resizeObserver: ResizeObserver | null;\n\n // Stored dimensions to avoid calling getBoundingClientRect redundantly.\n\n _cachedWidth: number;\n _cachedLeft: number;\n\n _rippleRadius: number = 24;\n\n // The value indicator tooltip text for the visual slider thumb(s).\n\n /** @docs-private */\n protected startValueIndicatorText: string = '';\n\n /** @docs-private */\n protected endValueIndicatorText: string = '';\n\n // Used to control the translateX of the visual slider thumb(s).\n\n _endThumbTransform: string;\n _startThumbTransform: string;\n\n _isRange: boolean = false;\n\n /** Whether the slider is rtl. */\n _isRtl: boolean = false;\n\n private _hasViewInitialized: boolean = false;\n\n /**\n * The width of the tick mark track.\n * The tick mark track width is different from full track width\n */\n _tickMarkTrackWidth: number = 0;\n\n _hasAnimation: boolean = false;\n\n private _resizeTimer: null | ReturnType<typeof setTimeout> = null;\n\n private _platform = inject(Platform);\n\n constructor(...args: unknown[]);\n\n constructor() {\n inject(_CdkPrivateStyleLoader).load(_StructuralStylesLoader);\n const animationMode = inject(ANIMATION_MODULE_TYPE, {optional: true});\n this._noopAnimations = animationMode === 'NoopAnimations';\n\n if (this._dir) {\n this._dirChangeSubscription = this._dir.change.subscribe(() => this._onDirChange());\n this._isRtl = this._dir.value === 'rtl';\n }\n }\n\n /** The radius of the native slider's knob. AFAIK there is no way to avoid hardcoding this. */\n _knobRadius: number = 8;\n\n _inputPadding: number;\n\n ngAfterViewInit(): void {\n if (this._platform.isBrowser) {\n this._updateDimensions();\n }\n\n const eInput = this._getInput(_MatThumb.END);\n const sInput = this._getInput(_MatThumb.START);\n this._isRange = !!eInput && !!sInput;\n this._cdr.detectChanges();\n\n if (typeof ngDevMode === 'undefined' || ngDevMode) {\n _validateInputs(\n this._isRange,\n this._getInput(_MatThumb.END),\n this._getInput(_MatThumb.START),\n );\n }\n\n const thumb = this._getThumb(_MatThumb.END);\n this._rippleRadius = thumb._ripple.radius;\n this._inputPadding = this._rippleRadius - this._knobRadius;\n\n this._isRange\n ? this._initUIRange(eInput as _MatSliderRangeThumb, sInput as _MatSliderRangeThumb)\n : this._initUINonRange(eInput!);\n\n this._updateTrackUI(eInput!);\n this._updateTickMarkUI();\n this._updateTickMarkTrackUI();\n\n this._observeHostResize();\n this._cdr.detectChanges();\n }\n\n private _initUINonRange(eInput: _MatSliderThumb): void {\n eInput.initProps();\n eInput.initUI();\n\n this._updateValueIndicatorUI(eInput);\n\n this._hasViewInitialized = true;\n eInput._updateThumbUIByValue();\n }\n\n private _initUIRange(eInput: _MatSliderRangeThumb, sInput: _MatSliderRangeThumb): void {\n eInput.initProps();\n eInput.initUI();\n\n sInput.initProps();\n sInput.initUI();\n\n eInput._updateMinMax();\n sInput._updateMinMax();\n\n eInput._updateStaticStyles();\n sInput._updateStaticStyles();\n\n this._updateValueIndicatorUIs();\n\n this._hasViewInitialized = true;\n\n eInput._updateThumbUIByValue();\n sInput._updateThumbUIByValue();\n }\n\n ngOnDestroy(): void {\n this._dirChangeSubscription.unsubscribe();\n this._resizeObserver?.disconnect();\n this._resizeObserver = null;\n }\n\n /** Handles updating the slider ui after a dir change. */\n private _onDirChange(): void {\n this._isRtl = this._dir?.value === 'rtl';\n this._isRange ? this._onDirChangeRange() : this._onDirChangeNonRange();\n this._updateTickMarkUI();\n }\n\n private _onDirChangeRange(): void {\n const endInput = this._getInput(_MatThumb.END) as _MatSliderRangeThumb;\n const startInput = this._getInput(_MatThumb.START) as _MatSliderRangeThumb;\n\n endInput._setIsLeftThumb();\n startInput._setIsLeftThumb();\n\n endInput.translateX = endInput._calcTranslateXByValue();\n startInput.translateX = startInput._calcTranslateXByValue();\n\n endInput._updateStaticStyles();\n startInput._updateStaticStyles();\n\n endInput._updateWidthInactive();\n startInput._updateWidthInactive();\n\n endInput._updateThumbUIByValue();\n startInput._updateThumbUIByValue();\n }\n\n private _onDirChangeNonRange(): void {\n const input = this._getInput(_MatThumb.END)!;\n input._updateThumbUIByValue();\n }\n\n /** Starts observing and updating the slider if the host changes its size. */\n private _observeHostResize() {\n if (typeof ResizeObserver === 'undefined' || !ResizeObserver) {\n return;\n }\n\n this._ngZone.runOutsideAngular(() => {\n this._resizeObserver = new ResizeObserver(() => {\n if (this._isActive()) {\n return;\n }\n if (this._resizeTimer) {\n clearTimeout(this._resizeTimer);\n }\n this._onResize();\n });\n this._resizeObserver.observe(this._elementRef.nativeElement);\n });\n }\n\n /** Whether any of the thumbs are currently active. */\n private _isActive(): boolean {\n return this._getThumb(_MatThumb.START)._isActive || this._getThumb(_MatThumb.END)._isActive;\n }\n\n private _getValue(thumbPosition: _MatThumb = _MatThumb.END): number {\n const input = this._getInput(thumbPosition);\n if (!input) {\n return this.min;\n }\n return input.value;\n }\n\n private _skipUpdate(): boolean {\n return !!(\n this._getInput(_MatThumb.START)?._skipUIUpdate || this._getInput(_MatThumb.END)?._skipUIUpdate\n );\n }\n\n /** Stores the slider dimensions. */\n _updateDimensions(): void {\n this._cachedWidth = this._elementRef.nativeElement.offsetWidth;\n this._cachedLeft = this._elementRef.nativeElement.getBoundingClientRect().left;\n }\n\n /** Sets the styles for the active portion of the track. */\n _setTrackActiveStyles(styles: {\n left: string;\n right: string;\n transform: string;\n transformOrigin: string;\n }): void {\n const trackStyle = this._trackActive.nativeElement.style;\n\n trackStyle.left = styles.left;\n trackStyle.right = styles.right;\n trackStyle.transformOrigin = styles.transformOrigin;\n trackStyle.transform = styles.transform;\n }\n\n /** Returns the translateX positioning for a tick mark based on it's index. */\n _calcTickMarkTransform(index: number): string {\n // TODO(wagnermaciel): See if we can avoid doing this and just using flex to position these.\n const offset = index * (this._tickMarkTrackWidth / (this._tickMarks.length - 1));\n const translateX = this._isRtl ? this._cachedWidth - 6 - offset : offset;\n return `translateX(${translateX}px`;\n }\n\n // Handlers for updating the slider ui.\n\n _onTranslateXChange(source: _MatSliderThumb): void {\n if (!this._hasViewInitialized) {\n return;\n }\n\n this._updateThumbUI(source);\n this._updateTrackUI(source);\n this._updateOverlappingThumbUI(source as _MatSliderRangeThumb);\n }\n\n _onTranslateXChangeBySideEffect(\n input1: _MatSliderRangeThumb,\n input2: _MatSliderRangeThumb,\n ): void {\n if (!this._hasViewInitialized) {\n return;\n }\n\n input1._updateThumbUIByValue();\n input2._updateThumbUIByValue();\n }\n\n _onValueChange(source: _MatSliderThumb): void {\n if (!this._hasViewInitialized) {\n return;\n }\n\n this._updateValueIndicatorUI(source);\n this._updateTickMarkUI();\n this._cdr.detectChanges();\n }\n\n _onMinMaxOrStepChange(): void {\n if (!this._hasViewInitialized) {\n return;\n }\n\n this._updateTickMarkUI();\n this._updateTickMarkTrackUI();\n this._cdr.markForCheck();\n }\n\n _onResize(): void {\n if (!this._hasViewInitialized) {\n return;\n }\n\n this._updateDimensions();\n if (this._isRange) {\n const eInput = this._getInput(_MatThumb.END) as _MatSliderRangeThumb;\n const sInput = this._getInput(_MatThumb.START) as _MatSliderRangeThumb;\n\n eInput._updateThumbUIByValue();\n sInput._updateThumbUIByValue();\n\n eInput._updateStaticStyles();\n sInput._updateStaticStyles();\n\n eInput._updateMinMax();\n sInput._updateMinMax();\n\n eInput._updateWidthInactive();\n sInput._updateWidthInactive();\n } else {\n const eInput = this._getInput(_MatThumb.END);\n if (eInput) {\n eInput._updateThumbUIByValue();\n }\n }\n\n this._updateTickMarkUI();\n this._updateTickMarkTrackUI();\n this._cdr.detectChanges();\n }\n\n /** Whether or not the slider thumbs overlap. */\n private _thumbsOverlap: boolean = false;\n\n /** Returns true if the slider knobs are overlapping one another. */\n private _areThumbsOverlapping(): boolean {\n const startInput = this._getInput(_MatThumb.START);\n const endInput = this._getInput(_MatThumb.END);\n if (!startInput || !endInput) {\n return false;\n }\n return endInput.translateX - startInput.translateX < 20;\n }\n\n /**\n * Updates the class names of overlapping slider thumbs so\n * that the current active thumb is styled to be on \"top\".\n */\n private _updateOverlappingThumbClassNames(source: _MatSliderRangeThumb): void {\n const sibling = source.getSibling()!;\n const sourceThumb = this._getThumb(source.thumbPosition);\n const siblingThumb = this._getThumb(sibling.thumbPosition);\n siblingThumb._hostElement.classList.remove('mdc-slider__thumb--top');\n sourceThumb._hostElement.classList.toggle('mdc-slider__thumb--top', this._thumbsOverlap);\n }\n\n /** Updates the UI of slider thumbs when they begin or stop overlapping. */\n private _updateOverlappingThumbUI(source: _MatSliderRangeThumb): void {\n if (!this._isRange || this._skipUpdate()) {\n return;\n }\n if (this._thumbsOverlap !== this._areThumbsOverlapping()) {\n this._thumbsOverlap = !this._thumbsOverlap;\n this._updateOverlappingThumbClassNames(source);\n }\n }\n\n // _MatThumb styles update conditions\n //\n // 1. TranslateX, resize, or dir change\n // - Reason: The thumb styles need to be updated according to the new translateX.\n // 2. Min, max, or step\n // - Reason: The value may have silently changed.\n\n /** Updates the translateX of the given thumb. */\n _updateThumbUI(source: _MatSliderThumb) {\n if (this._skipUpdate()) {\n return;\n }\n const thumb = this._getThumb(\n source.thumbPosition === _MatThumb.END ? _MatThumb.END : _MatThumb.START,\n )!;\n thumb._hostElement.style.transform = `translateX(${source.translateX}px)`;\n }\n\n // Value indicator text update conditions\n //\n // 1. Value\n // - Reason: The value displayed needs to be updated.\n // 2. Min, max, or step\n // - Reason: The value may have silently changed.\n\n /** Updates the value indicator tooltip ui for the given thumb. */\n _updateValueIndicatorUI(source: _MatSliderThumb): void {\n if (this._skipUpdate()) {\n return;\n }\n\n const valuetext = this.displayWith(source.value);\n\n this._hasViewInitialized\n ? source._valuetext.set(valuetext)\n : source._hostElement.setAttribute('aria-valuetext', valuetext);\n\n if (this.discrete) {\n source.thumbPosition === _MatThumb.START\n ? (this.startValueIndicatorText = valuetext)\n : (this.endValueIndicatorText = valuetext);\n\n const visualThumb = this._getThumb(source.thumbPosition);\n valuetext.length < 3\n ? visualThumb._hostElement.classList.add('mdc-slider__thumb--short-value')\n : visualThumb._hostElement.classList.remove('mdc-slider__thumb--short-value');\n }\n }\n\n /** Updates all value indicator UIs in the slider. */\n private _updateValueIndicatorUIs(): void {\n const eInput = this._getInput(_MatThumb.END);\n const sInput = this._getInput(_MatThumb.START);\n\n if (eInput) {\n this._updateValueIndicatorUI(eInput);\n }\n if (sInput) {\n this._updateValueIndicatorUI(sInput);\n }\n }\n\n // Update Tick Mark Track Width\n //\n // 1. Min, max, or step\n // - Reason: The maximum reachable value may have changed.\n // - Side note: The maximum reachable value is different from the maximum value set by the\n // user. For example, a slider with [min: 5, max: 100, step: 10] would have a maximum\n // reachable value of 95.\n // 2. Resize\n // - Reason: The position for the maximum reachable value needs to be recalculated.\n\n /** Updates the width of the tick mark track. */\n private _updateTickMarkTrackUI(): void {\n if (!this.showTickMarks || this._skipUpdate()) {\n return;\n }\n\n const step = this._step && this._step > 0 ? this._step : 1;\n const maxValue = Math.floor(this.max / step) * step;\n const percentage = (maxValue - this.min) / (this.max - this.min);\n this._tickMarkTrackWidth = (this._cachedWidth - 6) * percentage;\n }\n\n // Track active update conditions\n //\n // 1. TranslateX\n // - Reason: The track active should line up with the new thumb position.\n // 2. Min or max\n // - Reason #1: The 'active' percentage needs to be recalculated.\n // - Reason #2: The value may have silently changed.\n // 3. Step\n // - Reason: The value may have silently changed causing the thumb(s) to shift.\n // 4. Dir change\n // - Reason: The track active will need to be updated according to the new thumb position(s).\n // 5. Resize\n // - Reason: The total width the 'active' tracks translateX is based on has changed.\n\n /** Updates the scale on the active portion of the track. */\n _updateTrackUI(source: _MatSliderThumb): void {\n if (this._skipUpdate()) {\n return;\n }\n\n this._isRange\n ? this._updateTrackUIRange(source as _MatSliderRangeThumb)\n : this._updateTrackUINonRange(source as _MatSliderThumb);\n }\n\n private _updateTrackUIRange(source: _MatSliderRangeThumb): void {\n const sibling = source.getSibling();\n if (!sibling || !this._cachedWidth) {\n return;\n }\n\n const activePercentage = Math.abs(sibling.translateX - source.translateX) / this._cachedWidth;\n\n if (source._isLeftThumb && this._cachedWidth) {\n this._setTrackActiveStyles({\n left: 'auto',\n right: `${this._cachedWidth - sibling.translateX}px`,\n transformOrigin: 'right',\n transform: `scaleX(${activePercentage})`,\n });\n } else {\n this._setTrackActiveStyles({\n left: `${sibling.translateX}px`,\n right: 'auto',\n transformOrigin: 'left',\n transform: `scaleX(${activePercentage})`,\n });\n }\n }\n\n private _updateTrackUINonRange(source: _MatSliderThumb): void {\n this._isRtl\n ? this._setTrackActiveStyles({\n left: 'auto',\n right: '0px',\n transformOrigin: 'right',\n transform: `scaleX(${1 - source.fillPercentage})`,\n })\n : this._setTrackActiveStyles({\n left: '0px',\n right: 'auto',\n transformOrigin: 'left',\n transform: `scaleX(${source.fillPercentage})`,\n });\n }\n\n // Tick mark update conditions\n //\n // 1. Value\n // - Reason: a tick mark which was once active might now be inactive or vice versa.\n // 2. Min, max, or step\n // - Reason #1: the number of tick marks may have changed.\n // - Reason #2: The value may have silently changed.\n\n /** Updates the dots along the slider track. */\n _updateTickMarkUI(): void {\n if (\n !this.showTickMarks ||\n this.step === undefined ||\n this.min === undefined ||\n this.max === undefined\n ) {\n return;\n }\n const step = this.step > 0 ? this.step : 1;\n this._isRange ? this._updateTickMarkUIRange(step) : this._updateTickMarkUINonRange(step);\n }\n\n private _updateTickMarkUINonRange(step: number): void {\n const value = this._getValue();\n let numActive = Math.max(Math.round((value - this.min) / step), 0) + 1;\n let numInactive = Math.max(Math.round((this.max - value) / step), 0) - 1;\n this._isRtl ? numActive++ : numInactive++;\n\n this._tickMarks = Array(numActive)\n .fill(_MatTickMark.ACTIVE)\n .concat(Array(numInactive).fill(_MatTickMark.INACTIVE));\n }\n\n private _updateTickMarkUIRange(step: number): void {\n const endValue = this._getValue();\n const startValue = this._getValue(_MatThumb.START);\n\n const numInactiveBeforeStartThumb = Math.max(Math.round((startValue - this.min) / step), 0);\n const numActive = Math.max(Math.round((endValue - startValue) / step) + 1, 0);\n const numInactiveAfterEndThumb = Math.max(Math.round((this.max - endValue) / step), 0);\n this._tickMarks = Array(numInactiveBeforeStartThumb)\n .fill(_MatTickMark.INACTIVE)\n .concat(\n Array(numActive).fill(_MatTickMark.ACTIVE),\n Array(numInactiveAfterEndThumb).fill(_MatTickMark.INACTIVE),\n );\n }\n\n /** Gets the slider thumb input of the given thumb position. */\n _getInput(thumbPosition: _MatThumb): _MatSliderThumb | _MatSliderRangeThumb | undefined {\n if (thumbPosition === _MatThumb.END && this._input) {\n return this._input;\n }\n if (this._inputs?.length) {\n return thumbPosition === _MatThumb.START ? this._inputs.first : this._inputs.last;\n }\n return;\n }\n\n /** Gets the slider thumb HTML input element of the given thumb position. */\n _getThumb(thumbPosition: _MatThumb): _MatSliderVisualThumb {\n return thumbPosition === _MatThumb.END ? this._thumbs?.last! : this._thumbs?.first!;\n }\n\n _setTransition(withAnimation: boolean): void {\n this._hasAnimation = !this._platform.IOS && withAnimation && !this._noopAnimations;\n this._elementRef.nativeElement.classList.toggle(\n 'mat-mdc-slider-with-animation',\n this._hasAnimation,\n );\n }\n\n /** Whether the given pointer event occurred within the bounds of the slider pointer's DOM Rect. */\n _isCursorOnSliderThumb(event: PointerEvent, rect: DOMRect) {\n const radius = rect.width / 2;\n const centerX = rect.x + radius;\n const centerY = rect.y + radius;\n const dx = event.clientX - centerX;\n const dy = event.clientY - centerY;\n return Math.pow(dx, 2) + Math.pow(dy, 2) < Math.pow(radius, 2);\n }\n}\n\n/** Ensures that there is not an invalid configuration for the slider thumb inputs. */\nfunction _validateInputs(\n isRange: boolean,\n endInputElement: _MatSliderThumb | _MatSliderRangeThumb | undefined,\n startInputElement: _MatSliderThumb | undefined,\n): void {\n const startValid =\n !isRange || startInputElement?._hostElement.hasAttribute('matSliderStartThumb');\n const endValid = endInputElement?._hostElement.hasAttribute(\n isRange ? 'matSliderEndThumb' : 'matSliderThumb',\n );\n\n if (!startValid || !endValid) {\n _throwInvalidInputConfigurationError();\n }\n}\n\nfunction _throwInvalidInputConfigurationError(): void {\n throw Error(`Invalid slider thumb input configuration!\n\n Valid configurations are as follows:\n\