From 7dfb7ca562e7198a8eaf160c725ed54fc419a13f Mon Sep 17 00:00:00 2001 From: Timon Borter Date: Mon, 13 Mar 2023 21:33:23 +0100 Subject: [PATCH] feat(#163): support for action buttons --- .../core/src/datetimepicker/calendar.html | 1 + .../core/src/datetimepicker/calendar.scss | 94 +++++++++---------- .../datetimepicker-actions.component.html | 18 ++++ .../datetimepicker-actions.component.scss | 4 + .../datetimepicker-actions.component.ts | 36 +++++++ .../datetimepicker-content.html | 1 + .../datetimepicker-content.scss | 2 +- .../datetimepicker/datetimepicker.module.ts | 3 + .../core/src/datetimepicker/datetimepicker.ts | 52 +++++++++- projects/core/src/datetimepicker/index.ts | 1 + src/app/date.component.html | 70 +++++++++----- 11 files changed, 208 insertions(+), 74 deletions(-) create mode 100644 projects/core/src/datetimepicker/datetimepicker-actions.component.html create mode 100644 projects/core/src/datetimepicker/datetimepicker-actions.component.scss create mode 100644 projects/core/src/datetimepicker/datetimepicker-actions.component.ts diff --git a/projects/core/src/datetimepicker/calendar.html b/projects/core/src/datetimepicker/calendar.html index 4d3fd89c..3bb124de 100644 --- a/projects/core/src/datetimepicker/calendar.html +++ b/projects/core/src/datetimepicker/calendar.html @@ -144,4 +144,5 @@ [twelvehour]="twelvehour" > + diff --git a/projects/core/src/datetimepicker/calendar.scss b/projects/core/src/datetimepicker/calendar.scss index 61be2292..077fd60e 100644 --- a/projects/core/src/datetimepicker/calendar.scss +++ b/projects/core/src/datetimepicker/calendar.scss @@ -29,69 +29,69 @@ $mat-calendar-next-icon-transform: translateX(-2px) rotate(45deg); width: 150px; min-width: 150px; } -} -.mat-datetimepicker-calendar-header-year, -.mat-datetimepicker-calendar-header-date-time { - width: 100%; - font-weight: 500; - white-space: nowrap; -} + .mat-datetimepicker-calendar-header-year, + .mat-datetimepicker-calendar-header-date-time { + width: 100%; + font-weight: 500; + white-space: nowrap; + } -.mat-datetimepicker-calendar-header-year { - font-size: 16px; + .mat-datetimepicker-calendar-header-year { + font-size: 16px; - mat-icon { - transform: translateY(5px); + mat-icon { + transform: translateY(5px); + } } -} -.mat-datetimepicker-calendar-header-date-time { - font-size: 30px; - line-height: 34px; + .mat-datetimepicker-calendar-header-date-time { + font-size: 30px; + line-height: 34px; - [mode='landscape'] & { - white-space: normal; - word-wrap: break-word; + [mode='landscape'] & { + white-space: normal; + word-wrap: break-word; + } } -} - -.mat-datetimepicker-calendar-header-ampm-container { - font-size: 0.77em; -} -.mat-datetimepicker-calendar-header-year, -.mat-datetimepicker-calendar-header-date, -.mat-datetimepicker-calendar-header-hours, -.mat-datetimepicker-calendar-header-minutes, -.mat-datetimepicker-calendar-header-ampm { - &:not(.active) { - cursor: pointer; - opacity: 0.6; + .mat-datetimepicker-calendar-header-ampm-container { + font-size: 0.77em; } - &.not-clickable { - cursor: initial; + .mat-datetimepicker-calendar-header-year, + .mat-datetimepicker-calendar-header-date, + .mat-datetimepicker-calendar-header-hours, + .mat-datetimepicker-calendar-header-minutes, + .mat-datetimepicker-calendar-header-ampm { + &:not(.active) { + cursor: pointer; + opacity: 0.6; + } + + &.not-clickable { + cursor: initial; + } } -} -.mat-datetimepicker-calendar-header-time { - padding-left: 8px; + .mat-datetimepicker-calendar-header-time { + padding-left: 8px; - &:not(.active) { - opacity: 0.6; + &:not(.active) { + opacity: 0.6; - .mat-datetimepicker-calendar-header-hours, - .mat-datetimepicker-calendar-header-minutes, - .mat-datetimepicker-calendar-header-ampm { - cursor: pointer; - opacity: 1; + .mat-datetimepicker-calendar-header-hours, + .mat-datetimepicker-calendar-header-minutes, + .mat-datetimepicker-calendar-header-ampm { + cursor: pointer; + opacity: 1; + } } - } - [mode='landscape'] & { - display: block; - padding-left: 0; + [mode='landscape'] & { + display: block; + padding-left: 0; + } } } diff --git a/projects/core/src/datetimepicker/datetimepicker-actions.component.html b/projects/core/src/datetimepicker/datetimepicker-actions.component.html new file mode 100644 index 00000000..c660d621 --- /dev/null +++ b/projects/core/src/datetimepicker/datetimepicker-actions.component.html @@ -0,0 +1,18 @@ +
+ + +
diff --git a/projects/core/src/datetimepicker/datetimepicker-actions.component.scss b/projects/core/src/datetimepicker/datetimepicker-actions.component.scss new file mode 100644 index 00000000..35a07f06 --- /dev/null +++ b/projects/core/src/datetimepicker/datetimepicker-actions.component.scss @@ -0,0 +1,4 @@ +.mat-datetimepicker-actions { + display: flex; + justify-content: flex-end; +} diff --git a/projects/core/src/datetimepicker/datetimepicker-actions.component.ts b/projects/core/src/datetimepicker/datetimepicker-actions.component.ts new file mode 100644 index 00000000..ede80830 --- /dev/null +++ b/projects/core/src/datetimepicker/datetimepicker-actions.component.ts @@ -0,0 +1,36 @@ +import { Component, Input } from '@angular/core'; + +import { MatDatetimepickerComponent } from './datetimepicker'; + +@Component({ + selector: 'mat-datetimepicker-actions', + templateUrl: 'datetimepicker-actions.component.html', + styleUrls: ['datetimepicker-actions.component.scss'], +}) +export class MatDatetimepickerActionsComponent { + @Input() protected confirmButtonLabel = 'Confirm'; + @Input() protected cancelButtonLabel = 'Cancel'; + + private datetimepicker: MatDatetimepickerComponent; + + public fromTemplateWithDatetimepicker( + matDatetimepickerActions: MatDatetimepickerActionsComponent, + datetimepicker: MatDatetimepickerComponent + ) { + this.confirmButtonLabel = matDatetimepickerActions.confirmButtonLabel; + this.cancelButtonLabel = matDatetimepickerActions.cancelButtonLabel; + this.datetimepicker = datetimepicker; + } + + protected handleCancelButton(event): void { + event.preventDefault(); + this.datetimepicker.close(); + } + + protected handleConfirmButton(event): void { + event.preventDefault(); + // TODO: We need a "temporary" value from the input at this point + // TODO 2: The datetimepicker should not close itself after having everything selected + // this.datetimepicker._select(this.datetimepicker._selected) + } +} diff --git a/projects/core/src/datetimepicker/datetimepicker-content.html b/projects/core/src/datetimepicker/datetimepicker-content.html index bedd6ef0..3a8a766f 100644 --- a/projects/core/src/datetimepicker/datetimepicker-content.html +++ b/projects/core/src/datetimepicker/datetimepicker-content.html @@ -22,4 +22,5 @@ cdkTrapFocus class="mat-typography" > + diff --git a/projects/core/src/datetimepicker/datetimepicker-content.scss b/projects/core/src/datetimepicker/datetimepicker-content.scss index 751b9718..7c286610 100644 --- a/projects/core/src/datetimepicker/datetimepicker-content.scss +++ b/projects/core/src/datetimepicker/datetimepicker-content.scss @@ -5,8 +5,8 @@ $mat-datetimepicker-calendar-cell-size: 40px; $mat-datetimepicker-calendar-portrait-width: $mat-datetimepicker-calendar-cell-size * 7 + $mat-datetimepicker-calendar-padding * 2; $mat-datetimepicker-calendar-landscape-width: 446px; -$mat-datetimepicker-calendar-portrait-height: 405px; $mat-datetimepicker-calendar-landscape-height: 328px; +$mat-datetimepicker-calendar-portrait-height: 405px; .mat-datetimepicker-content { @include mat.elevation(8); diff --git a/projects/core/src/datetimepicker/datetimepicker.module.ts b/projects/core/src/datetimepicker/datetimepicker.module.ts index da8917e0..4a9c3bf2 100644 --- a/projects/core/src/datetimepicker/datetimepicker.module.ts +++ b/projects/core/src/datetimepicker/datetimepicker.module.ts @@ -10,6 +10,7 @@ import { MatDatetimepickerComponent, MatDatetimepickerContentComponent, } from './datetimepicker'; +import { MatDatetimepickerActionsComponent } from './datetimepicker-actions.component'; import { MatDatetimepickerInputDirective } from './datetimepicker-input'; import { MatDatetimepickerToggleComponent } from './datetimepicker-toggle'; import { MatDatetimepickerMonthViewComponent } from './month-view'; @@ -39,6 +40,7 @@ import { MatDialogModule } from '@angular/material/dialog'; MatDatetimepickerMonthViewComponent, MatDatetimepickerYearViewComponent, MatDatetimepickerMultiYearViewComponent, + MatDatetimepickerActionsComponent, ], exports: [ MatDatetimepickerCalendarComponent, @@ -51,6 +53,7 @@ import { MatDialogModule } from '@angular/material/dialog'; MatDatetimepickerMonthViewComponent, MatDatetimepickerYearViewComponent, MatDatetimepickerMultiYearViewComponent, + MatDatetimepickerActionsComponent, ], }) export class MatDatetimepickerModule {} diff --git a/projects/core/src/datetimepicker/datetimepicker.ts b/projects/core/src/datetimepicker/datetimepicker.ts index 1b4b9168..0a3f432e 100644 --- a/projects/core/src/datetimepicker/datetimepicker.ts +++ b/projects/core/src/datetimepicker/datetimepicker.ts @@ -14,6 +14,7 @@ import { ChangeDetectionStrategy, Component, ComponentRef, + ContentChildren, EventEmitter, Inject, Input, @@ -21,6 +22,7 @@ import { OnDestroy, Optional, Output, + QueryList, ViewChild, ViewContainerRef, ViewEncapsulation, @@ -29,7 +31,7 @@ import { MAT_DATEPICKER_SCROLL_STRATEGY } from '@angular/material/datepicker'; import { MatDialog, MatDialogRef } from '@angular/material/dialog'; import { Subject, Subscription } from 'rxjs'; import { first } from 'rxjs/operators'; -import { DatetimeAdapter } from '../adapter/datetime-adapter'; +import { DatetimeAdapter } from '../adapter'; import { MatCalendarView, MatDatetimepickerCalendarComponent, @@ -38,6 +40,7 @@ import { createMissingDateImplError } from './datetimepicker-errors'; import { MatDatetimepickerFilterType } from './datetimepicker-filtertype'; import { MatDatetimepickerInputDirective } from './datetimepicker-input'; import { MatDatetimepickerType } from './datetimepicker-type'; +import { MatDatetimepickerActionsComponent } from './datetimepicker-actions.component'; export type MatDatetimepickerMode = 'auto' | 'portrait' | 'landscape'; @@ -69,10 +72,30 @@ export class MatDatetimepickerContentComponent implements AfterContentInit { @ViewChild(MatDatetimepickerCalendarComponent, { static: true }) _calendar: MatDatetimepickerCalendarComponent; + @ViewChild('matDatetimepickerActions', { + read: ViewContainerRef, + static: true, + }) + private viewRef: ViewContainerRef; + ngAfterContentInit() { this._calendar._focusActiveCell(); } + public renderActions( + matDatetimepickerActions: MatDatetimepickerActionsComponent + ): void { + this.viewRef.clear(); + const matDatetimepickerActionsComponent = this.viewRef.createComponent( + MatDatetimepickerActionsComponent + ); + matDatetimepickerActionsComponent.instance.fromTemplateWithDatetimepicker( + matDatetimepickerActions, + this.datetimepicker + ); + matDatetimepickerActionsComponent.changeDetectorRef.detectChanges(); + } + onSelectionChange(date: D) { this.datetimepicker._select(date); this.datetimepicker.close(); @@ -99,7 +122,12 @@ export class MatDatetimepickerContentComponent implements AfterContentInit { encapsulation: ViewEncapsulation.None, preserveWhitespaces: false, }) -export class MatDatetimepickerComponent implements OnDestroy { +export class MatDatetimepickerComponent + implements AfterContentInit, OnDestroy +{ + @ContentChildren(MatDatetimepickerActionsComponent) + private actions: QueryList; + /** Active multi year view when click on year. */ @Input() multiYearSelector: boolean = false; /** if true change the clock to 12 hour format. */ @@ -166,6 +194,12 @@ export class MatDatetimepickerComponent implements OnDestroy { } } + ngAfterContentInit(): void { + if (this.actions.length > 1) { + throw Error('Cannot have more than one actions children!'); + } + } + private _startAt: D | null; /** The date to open the calendar to initially. */ @@ -315,11 +349,13 @@ export class MatDatetimepickerComponent implements OnDestroy { if (this.opened || this.disabled) { return; } + if (!this._datepickerInput) { throw Error( 'Attempted to open an MatDatepicker with no associated input.' ); } + if (this._document) { this._focusedElementBeforeOpen = this._document.activeElement; } @@ -380,6 +416,8 @@ export class MatDatetimepickerComponent implements OnDestroy { }); this._dialogRef.afterClosed().subscribe(() => this.close()); this._dialogRef.componentInstance.datetimepicker = this; + + this.attachActionsIfPresent(this._dialogRef.componentInstance); } /** Open the calendar as a popup. */ @@ -399,6 +437,8 @@ export class MatDatetimepickerComponent implements OnDestroy { this._popupRef.attach(this._calendarPortal); componentRef.instance.datetimepicker = this; + this.attachActionsIfPresent(componentRef.instance); + // Update the position once the calendar has rendered. this._ngZone.onStable .asObservable() @@ -461,4 +501,12 @@ export class MatDatetimepickerComponent implements OnDestroy { }, ]); } + + private attachActionsIfPresent( + componentInstance: MatDatetimepickerContentComponent + ) { + if (this.actions.length === 1) { + componentInstance.renderActions(this.actions.get(0)); + } + } } diff --git a/projects/core/src/datetimepicker/index.ts b/projects/core/src/datetimepicker/index.ts index 3125577e..7c710e80 100644 --- a/projects/core/src/datetimepicker/index.ts +++ b/projects/core/src/datetimepicker/index.ts @@ -3,6 +3,7 @@ export * from './clock'; export * from './calendar'; export * from './calendar-body'; export * from './datetimepicker'; +export * from './datetimepicker-actions.component'; export * from './datetimepicker-filtertype'; export * from './datetimepicker-input'; export * from './datetimepicker-toggle'; diff --git a/src/app/date.component.html b/src/app/date.component.html index 128cc85a..30eab680 100644 --- a/src/app/date.component.html +++ b/src/app/date.component.html @@ -25,14 +25,14 @@

required /> required + >required + min + >min + max + >max + placeholder="DateTime Year selector" /> required + >required + min + >min + max + >max + placeholder="Time AM/PM" /> required + >required + type="datetime" > required + >required + min + >min + max + >max + type="datetime" > required + >required + filter + >filter + type="datetime" > + + With Actions + + + + + +