Skip to content

Commit 7dfabca

Browse files
authored
fix(material/timepicker): add interface for timepicker input (#32050)
Adds an interface for the timepicker input so that users can provide their own if they want. This allows helps avoid a circular dependency. Fixes #31336.
1 parent 23378b0 commit 7dfabca

File tree

3 files changed

+53
-13
lines changed

3 files changed

+53
-13
lines changed

goldens/material/timepicker/index.api.md

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ export class MatTimepicker<D> implements OnDestroy, MatOptionParentComponent {
5959
readonly panelId: string;
6060
// (undocumented)
6161
protected _panelTemplate: Signal<TemplateRef<unknown>>;
62-
registerInput(input: MatTimepickerInput<D>): void;
62+
registerInput(input: MatTimepickerConnectedInput<D>): void;
6363
readonly selected: OutputEmitterRef<MatTimepickerSelected<D>>;
6464
protected _selectValue(option: MatOption<D>): void;
6565
// (undocumented)
@@ -77,15 +77,27 @@ export interface MatTimepickerConfig {
7777
}
7878

7979
// @public
80-
export class MatTimepickerInput<D> implements ControlValueAccessor, Validator, OnDestroy {
80+
export interface MatTimepickerConnectedInput<D> {
81+
disabled: Signal<boolean>;
82+
focus(): void;
83+
getLabelId(): string | null;
84+
getOverlayOrigin(): ElementRef<HTMLElement>;
85+
max: Signal<D | null>;
86+
min: Signal<D | null>;
87+
timepickerValueAssigned(value: D | null): void;
88+
value: Signal<D | null>;
89+
}
90+
91+
// @public
92+
export class MatTimepickerInput<D> implements MatTimepickerConnectedInput<D>, ControlValueAccessor, Validator, OnDestroy {
8193
constructor();
8294
protected readonly _ariaActiveDescendant: Signal<string | null>;
8395
protected readonly _ariaControls: Signal<string | null>;
8496
protected readonly _ariaExpanded: Signal<string>;
8597
readonly disabled: Signal<boolean>;
8698
readonly disabledInput: InputSignalWithTransform<boolean, unknown>;
8799
focus(): void;
88-
_getLabelId(): string | null;
100+
getLabelId(): string | null;
89101
getOverlayOrigin(): ElementRef<HTMLElement>;
90102
protected _handleBlur(): void;
91103
protected _handleInput(event: Event): void;
@@ -100,7 +112,7 @@ export class MatTimepickerInput<D> implements ControlValueAccessor, Validator, O
100112
registerOnValidatorChange(fn: () => void): void;
101113
setDisabledState(isDisabled: boolean): void;
102114
readonly timepicker: InputSignal<MatTimepicker<D>>;
103-
_timepickerValueAssigned(value: D | null): void;
115+
timepickerValueAssigned(value: D | null): void;
104116
validate(control: AbstractControl): ValidationErrors | null;
105117
readonly value: ModelSignal<D | null>;
106118
writeValue(value: any): void;

src/material/timepicker/timepicker-input.ts

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ import {
3636
Validators,
3737
} from '@angular/forms';
3838
import {MAT_FORM_FIELD} from '../form-field';
39-
import {MatTimepicker} from './timepicker';
39+
import {MatTimepicker, MatTimepickerConnectedInput} from './timepicker';
4040
import {MAT_INPUT_VALUE_ACCESSOR} from '../input';
4141
import {Subscription} from 'rxjs';
4242
import {DOWN_ARROW, ESCAPE, hasModifierKey, UP_ARROW} from '@angular/cdk/keycodes';
@@ -80,7 +80,9 @@ import {_getFocusedElementPierceShadowDom} from '@angular/cdk/platform';
8080
},
8181
],
8282
})
83-
export class MatTimepickerInput<D> implements ControlValueAccessor, Validator, OnDestroy {
83+
export class MatTimepickerInput<D>
84+
implements MatTimepickerConnectedInput<D>, ControlValueAccessor, Validator, OnDestroy
85+
{
8486
private _elementRef = inject<ElementRef<HTMLInputElement>>(ElementRef);
8587
private _dateAdapter = inject<DateAdapter<D>>(DateAdapter, {optional: true})!;
8688
private _dateFormats = inject(MAT_DATE_FORMATS, {optional: true})!;
@@ -258,7 +260,7 @@ export class MatTimepickerInput<D> implements ControlValueAccessor, Validator, O
258260
}
259261

260262
/** Gets the ID of the input's label. */
261-
_getLabelId(): string | null {
263+
getLabelId(): string | null {
262264
return this._formField?.getLabelId() || null;
263265
}
264266

@@ -318,7 +320,7 @@ export class MatTimepickerInput<D> implements ControlValueAccessor, Validator, O
318320
}
319321

320322
/** Called by the timepicker to sync up the user-selected value. */
321-
_timepickerValueAssigned(value: D | null) {
323+
timepickerValueAssigned(value: D | null) {
322324
if (!this._dateAdapter.sameTime(value, this.value())) {
323325
this._assignUserSelection(value, true);
324326
this._formatValue(value);

src/material/timepicker/timepicker.ts

Lines changed: 31 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,6 @@ import {TemplatePortal} from '@angular/cdk/portal';
5353
import {_getEventTarget} from '@angular/cdk/platform';
5454
import {ENTER, ESCAPE, hasModifierKey, TAB} from '@angular/cdk/keycodes';
5555
import {_IdGenerator, ActiveDescendantKeyManager} from '@angular/cdk/a11y';
56-
import type {MatTimepickerInput} from './timepicker-input';
5756
import {
5857
generateOptions,
5958
MAT_TIMEPICKER_CONFIG,
@@ -81,6 +80,33 @@ export const MAT_TIMEPICKER_SCROLL_STRATEGY = new InjectionToken<() => ScrollStr
8180
},
8281
);
8382

83+
/** Represents an input that is connected to a `mat-timepicker`. */
84+
export interface MatTimepickerConnectedInput<D> {
85+
/** Current value of the input. */
86+
value: Signal<D | null>;
87+
88+
/** Minimum allowed time. */
89+
min: Signal<D | null>;
90+
91+
/** Maximum allowed time. */
92+
max: Signal<D | null>;
93+
94+
/** Whether the input is disabled. */
95+
disabled: Signal<boolean>;
96+
97+
/** Focuses the input. */
98+
focus(): void;
99+
100+
/** Gets the element to which to connect the timepicker overlay. */
101+
getOverlayOrigin(): ElementRef<HTMLElement>;
102+
103+
/** Gets the ID of the input's label. */
104+
getLabelId(): string | null;
105+
106+
/** Callback invoked when the timepicker assigns a value. */
107+
timepickerValueAssigned(value: D | null): void;
108+
}
109+
84110
/**
85111
* Renders out a listbox that can be used to select a time of day.
86112
* Intended to be used together with `MatTimepickerInput`.
@@ -113,7 +139,7 @@ export class MatTimepicker<D> implements OnDestroy, MatOptionParentComponent {
113139
private _isOpen = signal(false);
114140
private _activeDescendant = signal<string | null>(null);
115141

116-
private _input = signal<MatTimepickerInput<D> | null>(null);
142+
private _input = signal<MatTimepickerConnectedInput<D> | null>(null);
117143
private _overlayRef: OverlayRef | null = null;
118144
private _portal: TemplatePortal<unknown> | null = null;
119145
private _optionsCacheKey: string | null = null;
@@ -269,7 +295,7 @@ export class MatTimepicker<D> implements OnDestroy, MatOptionParentComponent {
269295
}
270296

271297
/** Registers an input with the timepicker. */
272-
registerInput(input: MatTimepickerInput<D>): void {
298+
registerInput(input: MatTimepickerConnectedInput<D>): void {
273299
const currentInput = this._input();
274300

275301
if (currentInput && input !== currentInput && (typeof ngDevMode === 'undefined' || ngDevMode)) {
@@ -297,7 +323,7 @@ export class MatTimepicker<D> implements OnDestroy, MatOptionParentComponent {
297323
}
298324
});
299325
// Notify the input first so it can sync up the form control before emitting to `selected`.
300-
this._input()?._timepickerValueAssigned(option.value);
326+
this._input()?.timepickerValueAssigned(option.value);
301327
this.selected.emit({value: option.value, source: this});
302328
this._input()?.focus();
303329
}
@@ -307,7 +333,7 @@ export class MatTimepicker<D> implements OnDestroy, MatOptionParentComponent {
307333
if (this.ariaLabel()) {
308334
return null;
309335
}
310-
return this.ariaLabelledby() || this._input()?._getLabelId() || null;
336+
return this.ariaLabelledby() || this._input()?.getLabelId() || null;
311337
}
312338

313339
/** Handles animation events coming from the panel. */

0 commit comments

Comments
 (0)