From 68553c615288ec7285b8501172335ccc91505eb9 Mon Sep 17 00:00:00 2001 From: Kristiyan Kostadinov Date: Tue, 28 Jan 2025 12:29:15 +0100 Subject: [PATCH] fix(material/timepicker): switch away from animations module Reworks the timepicker to move it away from the animations module for the dropdown animation. --- src/material/timepicker/timepicker.html | 6 ++- src/material/timepicker/timepicker.scss | 30 ++++++++++++++ src/material/timepicker/timepicker.ts | 41 +++++++++++-------- tools/public_api_guard/material/timepicker.md | 5 ++- 4 files changed, 62 insertions(+), 20 deletions(-) diff --git a/src/material/timepicker/timepicker.html b/src/material/timepicker/timepicker.html index 86d86a41a89e..7e248eae11f4 100644 --- a/src/material/timepicker/timepicker.html +++ b/src/material/timepicker/timepicker.html @@ -2,14 +2,16 @@
+ (animationend)="_handleAnimationEnd($event)"> @for (option of _timeOptions; track option.value) { {{option.label}} + (onSelectionChange)="_selectValue($event.source)">{{option.label}} }
diff --git a/src/material/timepicker/timepicker.scss b/src/material/timepicker/timepicker.scss index 4cea2ec88ebc..fd98d9c01294 100644 --- a/src/material/timepicker/timepicker.scss +++ b/src/material/timepicker/timepicker.scss @@ -2,6 +2,28 @@ @use '../core/tokens/token-utils'; @use '../core/tokens/m2/mat/timepicker' as tokens-mat-timepicker; +@keyframes _mat-timepicker-enter { + from { + opacity: 0; + transform: scaleY(0.8); + } + + to { + opacity: 1; + transform: none; + } +} + +@keyframes _mat-timepicker-exit { + from { + opacity: 1; + } + + to { + opacity: 0; + } +} + mat-timepicker { display: none; } @@ -38,6 +60,14 @@ mat-timepicker { } } +.mat-timepicker-panel-animations-enabled { + animation: _mat-timepicker-enter 120ms cubic-bezier(0, 0, 0.2, 1); + + &.mat-timepicker-panel-exit { + animation: _mat-timepicker-exit 100ms linear; + } +} + .mat-timepicker-input[readonly] { cursor: pointer; } diff --git a/src/material/timepicker/timepicker.ts b/src/material/timepicker/timepicker.ts index 2a32ca0837b9..9d538213a793 100644 --- a/src/material/timepicker/timepicker.ts +++ b/src/material/timepicker/timepicker.ts @@ -9,6 +9,7 @@ import { afterNextRender, AfterRenderRef, + ANIMATION_MODULE_TYPE, booleanAttribute, ChangeDetectionStrategy, Component, @@ -32,7 +33,6 @@ import { ViewContainerRef, ViewEncapsulation, } from '@angular/core'; -import {animate, group, state, style, transition, trigger} from '@angular/animations'; import { DateAdapter, MAT_DATE_FORMATS, @@ -80,18 +80,6 @@ export interface MatTimepickerSelected { useExisting: MatTimepicker, }, ], - animations: [ - trigger('panel', [ - state('void', style({opacity: 0, transform: 'scaleY(0.8)'})), - transition(':enter', [ - group([ - animate('0.03s linear', style({opacity: 1})), - animate('0.12s cubic-bezier(0, 0, 0.2, 1)', style({transform: 'scaleY(1)'})), - ]), - ]), - transition(':leave', [animate('0.075s linear', style({opacity: 0}))]), - ]), - ], }) export class MatTimepicker implements OnDestroy, MatOptionParentComponent { private _overlay = inject(Overlay); @@ -101,6 +89,8 @@ export class MatTimepicker implements OnDestroy, MatOptionParentComponent { private _defaultConfig = inject(MAT_TIMEPICKER_CONFIG, {optional: true}); private _dateAdapter = inject>(DateAdapter, {optional: true})!; private _dateFormats = inject(MAT_DATE_FORMATS, {optional: true})!; + protected _animationsDisabled = + inject(ANIMATION_MODULE_TYPE, {optional: true}) === 'NoopAnimations'; private _isOpen = signal(false); private _activeDescendant = signal(null); @@ -246,8 +236,11 @@ export class MatTimepicker implements OnDestroy, MatOptionParentComponent { close(): void { if (this._isOpen()) { this._isOpen.set(false); - this._overlayRef?.detach(); this.closed.emit(); + + if (this._animationsDisabled) { + this._overlayRef?.detach(); + } } } @@ -270,9 +263,16 @@ export class MatTimepicker implements OnDestroy, MatOptionParentComponent { } /** Selects a specific time value. */ - protected _selectValue(value: D) { + protected _selectValue(option: MatOption) { this.close(); - this.selected.emit({value, source: this}); + this._keyManager.setActiveItem(option); + this._options().forEach(current => { + // This is primarily here so we don't show two selected options while animating away. + if (current !== option) { + current.deselect(false); + } + }); + this.selected.emit({value: option.value, source: this}); this._input()?.focus(); } @@ -284,6 +284,13 @@ export class MatTimepicker implements OnDestroy, MatOptionParentComponent { return this.ariaLabelledby() || this._input()?._getLabelId() || null; } + /** Handles animation events coming from the panel. */ + protected _handleAnimationEnd(event: AnimationEvent) { + if (event.animationName === '_mat-timepicker-exit') { + this._overlayRef?.detach(); + } + } + /** Creates an overlay reference for the timepicker panel. */ private _getOverlayRef(): OverlayRef { if (this._overlayRef) { @@ -409,7 +416,7 @@ export class MatTimepicker implements OnDestroy, MatOptionParentComponent { event.preventDefault(); if (this._keyManager.activeItem) { - this._selectValue(this._keyManager.activeItem.value); + this._selectValue(this._keyManager.activeItem); } else { this.close(); } diff --git a/tools/public_api_guard/material/timepicker.md b/tools/public_api_guard/material/timepicker.md index d99141df8ddc..d5c868014967 100644 --- a/tools/public_api_guard/material/timepicker.md +++ b/tools/public_api_guard/material/timepicker.md @@ -29,6 +29,8 @@ export const MAT_TIMEPICKER_CONFIG: InjectionToken; export class MatTimepicker implements OnDestroy, MatOptionParentComponent { constructor(); readonly activeDescendant: Signal; + // (undocumented) + protected _animationsDisabled: boolean; readonly ariaLabel: InputSignal; readonly ariaLabelledby: InputSignal; close(): void; @@ -36,6 +38,7 @@ export class MatTimepicker implements OnDestroy, MatOptionParentComponent { readonly disabled: Signal; readonly disableRipple: InputSignalWithTransform; protected _getAriaLabelledby(): string | null; + protected _handleAnimationEnd(event: AnimationEvent): void; readonly interval: InputSignalWithTransform; readonly isOpen: Signal; // (undocumented) @@ -50,7 +53,7 @@ export class MatTimepicker implements OnDestroy, MatOptionParentComponent { protected _panelTemplate: Signal>; registerInput(input: MatTimepickerInput): void; readonly selected: OutputEmitterRef>; - protected _selectValue(value: D): void; + protected _selectValue(option: MatOption): void; // (undocumented) protected _timeOptions: readonly MatTimepickerOption[]; // (undocumented)