Skip to content

Commit e5c4bc4

Browse files
committed
feat(material/timepicker): add option template
close #31209
1 parent 6990cc7 commit e5c4bc4

File tree

8 files changed

+57
-4
lines changed

8 files changed

+57
-4
lines changed

goldens/material/timepicker/index.api.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,8 @@ export class MatTimepicker<D> implements OnDestroy, MatOptionParentComponent {
5656
readonly options: InputSignal<readonly MatTimepickerOption<D>[] | null>;
5757
// (undocumented)
5858
protected _options: Signal<readonly MatOption<any>[]>;
59+
// (undocumented)
60+
protected _optionTemplate: Signal<TemplateRef<MatTimepickerOption<D>> | undefined>;
5961
readonly panelId: string;
6062
// (undocumented)
6163
protected _panelTemplate: Signal<TemplateRef<unknown>>;
@@ -65,7 +67,7 @@ export class MatTimepicker<D> implements OnDestroy, MatOptionParentComponent {
6567
// (undocumented)
6668
protected _timeOptions: readonly MatTimepickerOption<D>[];
6769
// (undocumented)
68-
static ɵcmp: i0.ɵɵComponentDeclaration<MatTimepicker<any>, "mat-timepicker", ["matTimepicker"], { "interval": { "alias": "interval"; "required": false; "isSignal": true; }; "options": { "alias": "options"; "required": false; "isSignal": true; }; "disableRipple": { "alias": "disableRipple"; "required": false; "isSignal": true; }; "ariaLabel": { "alias": "aria-label"; "required": false; "isSignal": true; }; "ariaLabelledby": { "alias": "aria-labelledby"; "required": false; "isSignal": true; }; }, { "selected": "selected"; "opened": "opened"; "closed": "closed"; }, never, never, true, never>;
70+
static ɵcmp: i0.ɵɵComponentDeclaration<MatTimepicker<any>, "mat-timepicker", ["matTimepicker"], { "interval": { "alias": "interval"; "required": false; "isSignal": true; }; "options": { "alias": "options"; "required": false; "isSignal": true; }; "disableRipple": { "alias": "disableRipple"; "required": false; "isSignal": true; }; "ariaLabel": { "alias": "aria-label"; "required": false; "isSignal": true; }; "ariaLabelledby": { "alias": "aria-labelledby"; "required": false; "isSignal": true; }; }, { "selected": "selected"; "opened": "opened"; "closed": "closed"; }, ["_optionTemplate"], never, true, never>;
6971
// (undocumented)
7072
static ɵfac: i0.ɵɵFactoryDeclaration<MatTimepicker<any>, never>;
7173
}

src/components-examples/material/timepicker/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,4 +5,5 @@ export {TimepickerValidationExample} from './timepicker-validation/timepicker-va
55
export {TimepickerOptionsExample} from './timepicker-options/timepicker-options-example';
66
export {TimepickerCustomIconExample} from './timepicker-custom-icon/timepicker-custom-icon-example';
77
export {TimepickerLocaleExample} from './timepicker-locale/timepicker-locale-example';
8+
export {TimepickerOptionTemplateExample} from './timepicker-option-template/timepicker-option-template-example';
89
export {TimepickerHarnessExample} from './timepicker-harness/timepicker-harness-example';
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
<mat-form-field>
2+
<mat-label>Pick a time</mat-label>
3+
<input matInput [matTimepicker]="picker">
4+
<mat-timepicker-toggle matIconSuffix [for]="picker"/>
5+
<mat-timepicker #picker>
6+
<ng-template let-option>
7+
<mat-icon>
8+
{{ (dateAdapter.compareTime(option.value, sunrise) > 0 && dateAdapter.compareTime(option.value, sunset) < 0) ? 'sunny' : 'bedtime' }}
9+
</mat-icon>
10+
{{ option.label }}
11+
</ng-template>
12+
</mat-timepicker>
13+
</mat-form-field>
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
import {ChangeDetectionStrategy, Component, inject} from '@angular/core';
2+
import {MatTimepickerModule} from '@angular/material/timepicker';
3+
import {MatIcon} from '@angular/material/icon';
4+
import {MatInputModule} from '@angular/material/input';
5+
import {MatFormFieldModule} from '@angular/material/form-field';
6+
import {DateAdapter, provideNativeDateAdapter} from '@angular/material/core';
7+
8+
/** @title Timepicker with option template. */
9+
@Component({
10+
selector: 'timepicker-option-template-example',
11+
templateUrl: 'timepicker-option-template-example.html',
12+
providers: [provideNativeDateAdapter()],
13+
imports: [MatFormFieldModule, MatIcon, MatInputModule, MatTimepickerModule],
14+
changeDetection: ChangeDetectionStrategy.OnPush,
15+
})
16+
export class TimepickerOptionTemplateExample {
17+
readonly dateAdapter = inject<DateAdapter<Date>>(DateAdapter);
18+
readonly sunrise: Date;
19+
readonly sunset: Date;
20+
21+
constructor() {
22+
this.sunrise = new Date();
23+
this.sunrise.setHours(7, 0, 0);
24+
this.sunset = new Date();
25+
this.sunset.setHours(17, 30, 0);
26+
}
27+
}

src/material/slider/_slider-theme.scss

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@
7979
@include token-utils.batch-create-token-values($tokens, _define-overrides());
8080
}
8181

82-
/// Outputs all (base, color, typography, and density) theme styles for the mat-option.
82+
/// Outputs all (base, color, typography, and density) theme styles for the mat-slider.
8383
/// @param {Map} $theme The theme to generate styles for.
8484
/// @param {String} $color-variant The color variant to use for the component (M3 only)
8585
@mixin theme($theme, $color-variant: null) {

src/material/timepicker/BUILD.bazel

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,7 @@ ng_project(
7777
":timepicker_css",
7878
],
7979
deps = [
80+
"//:node_modules/@angular/common",
8081
"//:node_modules/@angular/core",
8182
"//:node_modules/@angular/forms",
8283
"//:node_modules/rxjs",

src/material/timepicker/timepicker.html

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
<ng-template #defaultOptionTemplate let-option>{{ option.label }}</ng-template>
2+
13
<ng-template #panelTemplate>
24
<div
35
role="listbox"
@@ -11,7 +13,11 @@
1113
@for (option of _timeOptions; track option.value) {
1214
<mat-option
1315
[value]="option.value"
14-
(onSelectionChange)="_selectValue($event.source)">{{option.label}}</mat-option>
16+
(onSelectionChange)="_selectValue($event.source)">
17+
<ng-container [ngTemplateOutlet]="_optionTemplate() || defaultOptionTemplate"
18+
[ngTemplateOutletContext]="{ $implicit: option }">
19+
</ng-container>
20+
</mat-option>
1521
}
1622
</div>
1723
</ng-template>

src/material/timepicker/timepicker.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import {
1313
ChangeDetectionStrategy,
1414
Component,
1515
computed,
16+
contentChild,
1617
effect,
1718
ElementRef,
1819
inject,
@@ -33,6 +34,7 @@ import {
3334
ViewContainerRef,
3435
ViewEncapsulation,
3536
} from '@angular/core';
37+
import {NgTemplateOutlet} from '@angular/common';
3638
import {
3739
_animationsDisabled,
3840
DateAdapter,
@@ -92,7 +94,7 @@ export const MAT_TIMEPICKER_SCROLL_STRATEGY = new InjectionToken<() => ScrollStr
9294
styleUrl: 'timepicker.css',
9395
changeDetection: ChangeDetectionStrategy.OnPush,
9496
encapsulation: ViewEncapsulation.None,
95-
imports: [MatOption],
97+
imports: [NgTemplateOutlet, MatOption],
9698
providers: [
9799
{
98100
provide: MAT_OPTION_PARENT_COMPONENT,
@@ -123,6 +125,7 @@ export class MatTimepicker<D> implements OnDestroy, MatOptionParentComponent {
123125
protected _panelTemplate = viewChild.required<TemplateRef<unknown>>('panelTemplate');
124126
protected _timeOptions: readonly MatTimepickerOption<D>[] = [];
125127
protected _options = viewChildren(MatOption);
128+
protected _optionTemplate = contentChild<TemplateRef<MatTimepickerOption<D>>>(TemplateRef);
126129

127130
private _keyManager = new ActiveDescendantKeyManager(this._options, this._injector)
128131
.withHomeAndEnd(true)

0 commit comments

Comments
 (0)