From b6d6b51619d535f189633f312f13c512d168cd35 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=87etin?= <69278826+cetincakiroglu@users.noreply.github.com> Date: Thu, 25 Jul 2024 14:34:20 +0300 Subject: [PATCH] Theming | Add Calendar, add fluid #16092, remove touchUI fixes #16098, deprecate calendar fixes #16100 --- src/app/components/calendar/calendar.ts | 293 +++++------ .../calendar/style/datepickerstyle.ts | 467 ++++++++++++++++++ 2 files changed, 602 insertions(+), 158 deletions(-) create mode 100644 src/app/components/calendar/style/datepickerstyle.ts diff --git a/src/app/components/calendar/calendar.ts b/src/app/components/calendar/calendar.ts index bb05af05a81..684f0ea332c 100644 --- a/src/app/components/calendar/calendar.ts +++ b/src/app/components/calendar/calendar.ts @@ -9,6 +9,7 @@ import { ElementRef, EventEmitter, forwardRef, + inject, Inject, Input, NgModule, @@ -39,7 +40,9 @@ import { CalendarIcon } from 'primeng/icons/calendar'; import { Nullable, VoidListener } from 'primeng/ts-helpers'; import { NavigationState, CalendarResponsiveOptions, CalendarTypeView, LocaleSettings, Month, CalendarMonthChangeEvent, CalendarYearChangeEvent } from './calendar.interface'; import { AutoFocusModule } from 'primeng/autofocus'; -import { InputTextModule } from '../inputtext/inputtext'; +import { InputTextModule } from 'primeng/inputtext'; +import { DatePickerStyle } from './style/datepickerstyle'; +import { BaseComponent } from 'primeng/basecomponent'; export const CALENDAR_VALUE_ACCESSOR: any = { provide: NG_VALUE_ACCESSOR, @@ -51,21 +54,9 @@ export const CALENDAR_VALUE_ACCESSOR: any = { * @group Components */ @Component({ - selector: 'p-calendar, p-datepicker', + selector: 'p-calendar', template: ` - + - - + + - - - + - - - + + + + +
-
-
+
+
- - - + + + @@ -184,7 +164,7 @@ export const CALENDAR_VALUE_ACCESSOR: any = { (click)="switchToMonthView($event)" (keydown)="onContainerButtonKeydown($event)" *ngIf="currentView === 'date'" - styleClass="p-datepicker-month p-link" + styleClass="p-datepicker-select-month" [disabled]="switchViewButtonDisabled()" [attr.aria-label]="this.getTranslation('chooseMonth')" [text]="true" @@ -193,10 +173,10 @@ export const CALENDAR_VALUE_ACCESSOR: any = { - + - +
-
- - - - - - - - - - + + +
- {{ getTranslation('weekHeader') }} - - {{ weekDay }} -
- - {{ month.weekNumbers[j] }} + + + + + + + + + + + - - - -
+ {{ getTranslation('weekHeader') }} + + {{ weekDay }} +
+ + {{ month.weekNumbers[j] }} + + + + + {{ date.day }} + + + + + + - - - - {{ date.day }} - - - - - - - -
- {{ date.day }} -
-
-
- +
+ {{ date.day }} +
+ +
-
+
{{ m }} @@ -287,13 +263,12 @@ export const CALENDAR_VALUE_ACCESSOR: any = {
-
+
{{ y }} @@ -303,10 +278,10 @@ export const CALENDAR_VALUE_ACCESSOR: any = {
-
-
+
+
@@ -325,7 +299,7 @@ export const CALENDAR_VALUE_ACCESSOR: any = { 0{{ currentHour }}
-
+
{{ timeSeparator }}
-
+
@@ -367,8 +338,7 @@ export const CALENDAR_VALUE_ACCESSOR: any = { 0{{ currentMinute }}
-
+
{{ timeSeparator }}
-
+
@@ -410,8 +378,7 @@ export const CALENDAR_VALUE_ACCESSOR: any = { 0{{ currentSecond }}
-
- +
+ {{ timeSeparator }} +
+
+ - {{ pm ? 'PM' : 'AM' }} - + @@ -443,8 +411,8 @@ export const CALENDAR_VALUE_ACCESSOR: any = {
- - + +
@@ -474,18 +442,11 @@ export const CALENDAR_VALUE_ACCESSOR: any = { ]) ]) ], - host: { - class: 'p-element p-inputwrapper', - '[class.p-inputwrapper-filled]': 'filled', - '[class.p-inputwrapper-focus]': 'focus', - '[class.p-calendar-clearable]': 'showClear && !disabled' - }, - providers: [CALENDAR_VALUE_ACCESSOR], + providers: [CALENDAR_VALUE_ACCESSOR, DatePickerStyle], changeDetection: ChangeDetectionStrategy.OnPush, - encapsulation: ViewEncapsulation.None, - styleUrls: ['./calendar.css'] + encapsulation: ViewEncapsulation.None }) -export class Calendar implements OnInit, OnDestroy, ControlValueAccessor { +export class Calendar extends BaseComponent implements OnInit, OnDestroy, ControlValueAccessor { @Input() iconDisplay: 'input' | 'button' = 'button'; /** * Inline style of the component. @@ -578,6 +539,11 @@ export class Calendar implements OnInit, OnDestroy, ControlValueAccessor { * @group Props */ @Input({ transform: booleanAttribute }) showIcon: boolean | undefined; + /** + * Whether the component should span the full width of its parent. + * @group Props + */ + @Input({ transform: booleanAttribute }) fluid: boolean | undefined; /** * Icon of the calendar button. * @group Props @@ -689,12 +655,12 @@ export class Calendar implements OnInit, OnDestroy, ControlValueAccessor { * Style class of the today button. * @group Props */ - @Input() todayButtonStyleClass: string = 'p-button-text'; + @Input() todayButtonStyleClass: string | undefined; /** * Style class of the clear button. * @group Props */ - @Input() clearButtonStyleClass: string = 'p-button-text'; + @Input() clearButtonStyleClass: string | undefined; /** * When present, it specifies that the component should automatically get focus on load. * @group Props @@ -1021,6 +987,8 @@ export class Calendar implements OnInit, OnDestroy, ControlValueAccessor { } } + _componentStyle = inject(DatePickerStyle); + contentViewChild!: ElementRef; value: any; @@ -1087,6 +1055,10 @@ export class Calendar implements OnInit, OnDestroy, ControlValueAccessor { preventDocumentListener: Nullable; + dayClass(date) { + return this._componentStyle.classes.day({ instance: this, date: date }); + } + dateTemplate: Nullable>; headerTemplate: Nullable>; @@ -1173,19 +1145,21 @@ export class Calendar implements OnInit, OnDestroy, ControlValueAccessor { return this.currentView === 'year' ? this.getTranslation('nextDecade') : this.currentView === 'month' ? this.getTranslation('nextYear') : this.getTranslation('nextMonth'); } - constructor( - @Inject(DOCUMENT) private document: Document, - public el: ElementRef, - public renderer: Renderer2, - public cd: ChangeDetectorRef, - private zone: NgZone, - private config: PrimeNGConfig, - public overlayService: OverlayService - ) { - this.window = this.document.defaultView as Window; + get rootClass() { + return this._componentStyle.classes.root({ instance: this }); + } + + get panelClass() { + return this._componentStyle.classes.panel({ instance: this }); + } + + constructor(private zone: NgZone, public overlayService: OverlayService) { + super(); } ngOnInit() { + console.warn('Calendar component is deprecated as of v18, use DatePicker component instead.'); + super.ngOnInit(); this.attributeSelector = UniqueComponentId(); this.panelId = this.attributeSelector + '_panel'; const date = this.defaultDate || new Date(); @@ -1269,6 +1243,7 @@ export class Calendar implements OnInit, OnDestroy, ControlValueAccessor { } ngAfterViewInit() { + super.ngAfterViewInit(); if (this.inline) { this.contentViewChild && this.contentViewChild.nativeElement.setAttribute(this.attributeSelector, ''); @@ -3690,6 +3665,8 @@ export class Calendar implements OnInit, OnDestroy, ControlValueAccessor { this.clearTimePickerTimer(); this.restoreOverlayAppend(); this.onOverlayHide(); + + super.ngOnDestroy(); } } diff --git a/src/app/components/calendar/style/datepickerstyle.ts b/src/app/components/calendar/style/datepickerstyle.ts new file mode 100644 index 00000000000..f089e49d50a --- /dev/null +++ b/src/app/components/calendar/style/datepickerstyle.ts @@ -0,0 +1,467 @@ +import { Injectable } from '@angular/core'; +import { BaseStyle } from 'primeng/base'; + +const theme = ({ dt }) => ` +.p-datepicker { +position: relative; + display: inline-flex; + max-width: 100%; +} + +.p-datepicker-input { + flex: 1 1 auto; + width: 1%; +} + +.p-datepicker:has(.p-datepicker-dropdown) .p-datepicker-input { + border-top-right-radius: 0; + border-bottom-right-radius: 0; +} + +.p-datepicker-dropdown { + cursor: pointer; + display: inline-flex; + cursor: pointer; + user-select: none; + align-items: center; + justify-content: center; + overflow: hidden; + position: relative; + width: ${dt('datepicker.dropdown.width')}; + border-top-right-radius: ${dt('datepicker.dropdown.border.radius')}; + border-bottom-right-radius: ${dt('datepicker.dropdown.border.radius')}; + background: ${dt('datepicker.dropdown.background')}; + border: 1px solid ${dt('datepicker.dropdown.border.color')}; + border-left: 0 none; + color: ${dt('datepicker.dropdown.color')}; + transition: background ${dt('datepicker.transition.duration')}, color ${dt('datepicker.transition.duration')}, border-color ${dt('datepicker.transition.duration')}, outline-color ${dt('datepicker.transition.duration')}; + outline-color: transparent; +} + +.p-datepicker-dropdown:not(:disabled):hover { + background: ${dt('datepicker.dropdown.hover.background')}; + border-color: ${dt('datepicker.dropdown.hover.border.color')}; + color: ${dt('datepicker.dropdown.hover.color')}; +} + +.p-datepicker-dropdown:not(:disabled):active { + background: ${dt('datepicker.dropdown.active.background')}; + border-color: ${dt('datepicker.dropdown.active.border.color')}; + color: ${dt('datepicker.dropdown.active.color')}; +} + +.p-datepicker-dropdown:focus-visible { + box-shadow: ${dt('datepicker.dropdown.focus.ring.shadow')}; + outline: ${dt('datepicker.dropdown.focus.ring.width')} ${dt('datepicker.dropdown.focus.ring.style')} ${dt('datepicker.dropdown.focus.ring.color')}; + outline-offset: ${dt('datepicker.dropdown.focus.ring.offset')}; +} + +.p-datepicker:has(.p-datepicker-input-icon-container) { + position: relative; +} + +.p-datepicker:has(.p-datepicker-input-icon-container) .p-datepicker-input { + padding-right: calc((${dt('form.field.padding.x')} * 2) + ${dt('icon.size')}); +} + +.p-datepicker-input-icon-container { + cursor: pointer; + position: absolute; + top: 50%; + right: ${dt('form.field.padding.x')}; + margin-top: calc(-1 * (${dt('icon.size')} / 2)); + color: ${dt('datepicker.input.icon.color')}; +} + +.p-datepicker-fluid { + display: flex; +} + +.p-datepicker-fluid .p-datepicker-input { + width: 1%; +} + +.p-datepicker .p-datepicker-panel { + min-width: 100%; +} + +.p-datepicker-panel { + position: absolute; + width: auto; + padding: ${dt('datepicker.panel.padding')}; + background: ${dt('datepicker.panel.background')}; + color: ${dt('datepicker.panel.color')}; + border: 1px solid ${dt('datepicker.panel.border.color')}; + border-radius: ${dt('datepicker.panel.border.radius')}; + box-shadow: ${dt('datepicker.panel.shadow')}; +} + +.p-datepicker-panel-inline { + display: inline-block; + overflow-x: auto; + box-shadow: none; +} + +.p-datepicker-header { + display: flex; + align-items: center; + justify-content: space-between; + padding: ${dt('datepicker.header.padding')}; + font-weight: ${dt('datepicker.header.font.weight')}; + background: ${dt('datepicker.header.background')}; + color: ${dt('datepicker.header.color')}; + border-bottom: 1px solid ${dt('datepicker.header.border.color')}; +} + +.p-datepicker-title { + display: flex; + align-items: center; + justify-content: space-between; + gap: ${dt('datepicker.title.gap')}; + font-weight: ${dt('datepicker.title.font.weight')}; +} + +.p-datepicker-select-year, +.p-datepicker-select-month { + border: none; + background: transparent; + margin: 0; + cursor: pointer; + font-weight: inherit; + transition: background ${dt('datepicker.transition.duration')}, color ${dt('datepicker.transition.duration')}, border-color ${dt('datepicker.transition.duration')}, outline-color ${dt('datepicker.transition.duration')}, box-shadow ${dt( + 'datepicker.transition.duration' +)}; +} + +.p-datepicker-select-month { + padding: ${dt('datepicker.select.month.padding')}; + color: ${dt('datepicker.select.month.color')}; + border-radius: ${dt('datepicker.select.month.border.radius')}; +} + +.p-datepicker-select-year { + padding: ${dt('datepicker.select.year.padding')}; + color: ${dt('datepicker.select.year.color')}; + border-radius: ${dt('datepicker.select.year.border.radius')}; +} + +.p-datepicker-select-month:enabled:hover { + background: ${dt('datepicker.select.month.hover.background')}; + color: ${dt('datepicker.select.month.hover.color')}; +} + +.p-datepicker-select-year:enabled:hover { + background: ${dt('datepicker.select.year.hover.background')}; + color: ${dt('datepicker.select.year.hover.color')}; +} + +.p-datepicker-calendar-container { + display: flex; +} + +.p-datepicker-calendar-container .p-datepicker-calendar { + flex: 1 1 auto; + border-left: 1px solid ${dt('datepicker.group.border.color')}; + padding-right: ${dt('datepicker.group.gap')}; + padding-left: ${dt('datepicker.group.gap')}; +} + +.p-datepicker-calendar-container .p-datepicker-calendar:first-child { + padding-left: 0; + border-left: 0 none; +} + +.p-datepicker-calendar-container .p-datepicker-calendar:last-child { + padding-right: 0; +} + +.p-datepicker-day-view { + width: 100%; + border-collapse: collapse; + font-size: 1rem; + margin: ${dt('datepicker.day.view.margin')}; +} + +.p-datepicker-weekday-cell { + padding: ${dt('datepicker.week.day.padding')}; +} + +.p-datepicker-weekday { + font-weight: ${dt('datepicker.week.day.font.weight')}; + color: ${dt('datepicker.week.day.color')}; +} + +.p-datepicker-day-cell { + padding: ${dt('datepicker.date.padding')}; +} + +.p-datepicker-day { + display: flex; + justify-content: center; + align-items: center; + cursor: pointer; + margin: 0 auto; + overflow: hidden; + position: relative; + width: ${dt('datepicker.date.width')}; + height: ${dt('datepicker.date.height')}; + border-radius: ${dt('datepicker.date.border.radius')}; + transition: background ${dt('datepicker.transition.duration')}, color ${dt('datepicker.transition.duration')}, border-color ${dt('datepicker.transition.duration')}, + box-shadow ${dt('datepicker.transition.duration')}, outline-color ${dt('datepicker.transition.duration')}; + border: 1px solid transparent; + outline-color: transparent; + color: ${dt('datepicker.date.color')}; +} + +.p-datepicker-day:not(.p-datepicker-day-selected):not(.p-disabled):hover { + background: ${dt('datepicker.date.hover.background')}; + color: ${dt('datepicker.date.hover.color')}; +} + +.p-datepicker-day:focus-visible { + box-shadow: ${dt('datepicker.date.focus.ring.shadow')}; + outline: ${dt('datepicker.date.focus.ring.width')} ${dt('datepicker.date.focus.ring.style')} ${dt('datepicker.date.focus.ring.color')}; + outline-offset: ${dt('datepicker.date.focus.ring.offset')}; +} + +.p-datepicker-day-selected { + background: ${dt('datepicker.date.selected.background')}; + color: ${dt('datepicker.date.selected.color')}; +} + +.p-datepicker-day-selected-range { + background: ${dt('datepicker.date.range.selected.background')}; + color: ${dt('datepicker.date.range.selected.color')}; +} + +.p-datepicker-today > .p-datepicker-day { + background: ${dt('datepicker.today.background')}; + color: ${dt('datepicker.today.color')}; +} + +.p-datepicker-today > .p-datepicker-day-selected { + background: ${dt('datepicker.date.selected.background')}; + color: ${dt('datepicker.date.selected.color')}; +} + +.p-datepicker-today > .p-datepicker-day-selected-range { + background: ${dt('datepicker.date.range.selected.background')}; + color: ${dt('datepicker.date.range.selected.color')}; +} + +.p-datepicker-weeknumber { + text-align: center +} + +.p-datepicker-month-view { + margin: ${dt('datepicker.month.view.margin')}; +} + +.p-datepicker-month { + width: 33.3%; + display: inline-flex; + align-items: center; + justify-content: center; + cursor: pointer; + overflow: hidden; + position: relative; + padding: ${dt('datepicker.date.padding')}; + transition: background ${dt('datepicker.transition.duration')}, color ${dt('datepicker.transition.duration')}, border-color ${dt('datepicker.transition.duration')}, box-shadow ${dt('datepicker.transition.duration')}, outline-color ${dt( + 'datepicker.transition.duration' +)}; + border-radius: ${dt('datepicker.month.border.radius')}; + outline-color: transparent; + color: ${dt('datepicker.date.color')}; +} + +.p-datepicker-month:not(.p-disabled):not(.p-datepicker-month-selected):hover { + color: ${dt('datepicker.date.hover.color')}; + background: ${dt('datepicker.date.hover.background')}; +} + +.p-datepicker-month-selected { + color: ${dt('datepicker.date.selected.color')}; + background: ${dt('datepicker.date.selected.background')}; +} + +.p-datepicker-month:not(.p-disabled):focus-visible { + box-shadow: ${dt('datepicker.date.focus.ring.shadow')}; + outline: ${dt('datepicker.date.focus.ring.width')} ${dt('datepicker.date.focus.ring.style')} ${dt('datepicker.date.focus.ring.color')}; + outline-offset: ${dt('datepicker.date.focus.ring.offset')}; +} + +.p-datepicker-year-view { + margin: ${dt('datepicker.year.view.margin')}; +} + +.p-datepicker-year { + width: 50%; + display: inline-flex; + align-items: center; + justify-content: center; + cursor: pointer; + overflow: hidden; + position: relative; + padding: ${dt('datepicker.date.padding')}; + transition: background ${dt('datepicker.transition.duration')}, color ${dt('datepicker.transition.duration')}, border-color ${dt('datepicker.transition.duration')}, box-shadow ${dt('datepicker.transition.duration')}, outline-color ${dt( + 'datepicker.transition.duration' +)}; + border-radius: ${dt('datepicker.year.border.radius')}; + outline-color: transparent; + color: ${dt('datepicker.date.color')}; +} + +.p-datepicker-year:not(.p-disabled):not(.p-datepicker-year-selected):hover { + color: ${dt('datepicker.date.hover.color')}; + background: ${dt('datepicker.date.hover.background')}; +} + +.p-datepicker-year-selected { + color: ${dt('datepicker.date.selected.color')}; + background: ${dt('datepicker.date.selected.background')}; +} + +.p-datepicker-year:not(.p-disabled):focus-visible { + box-shadow: ${dt('datepicker.date.focus.ring.shadow')}; + outline: ${dt('datepicker.date.focus.ring.width')} ${dt('datepicker.date.focus.ring.style')} ${dt('datepicker.date.focus.ring.color')}; + outline-offset: ${dt('datepicker.date.focus.ring.offset')}; +} + +.p-datepicker-buttonbar { + display: flex; + justify-content: space-between; + align-items: center; + padding: ${dt('datepicker.buttonbar.padding')}; + border-top: 1px solid ${dt('datepicker.buttonbar.border.color')}; +} + +.p-datepicker-buttonbar .p-button { + width: auto; +} + +.p-datepicker-time-picker { + display: flex; + justify-content: center; + align-items: center; + border-top: 1px solid ${dt('datepicker.time.picker.border.color')}; + padding: 0; + gap: ${dt('datepicker.time.picker.gap')}; +} + +.p-datepicker-calendar-container + .p-datepicker-time-picker { + padding: ${dt('datepicker.time.picker.padding')}; +} + +.p-datepicker-time-picker > div { + display: flex; + align-items: center; + flex-direction: column; + gap: ${dt('datepicker.time.picker.button.gap')}; +} + +.p-datepicker-time-picker span { + font-size: 1rem; +} + +.p-datepicker-timeonly .p-datepicker-time-picker { + border-top: 0 none; +} +`; + +const inlineStyles = { + root: ({ props }) => ({ position: props.appendTo === 'self' ? 'relative' : undefined }) +}; + +const classes = { + root: ({ instance }) => ({ + 'p-datepicker p-component p-inputwrapper': true, + 'p-datepicker-fluid': instance.fluid, + 'p-inputwrapper-filled': instance.filled, + 'p-inputwrapper-focus': instance.focus, + 'p-focus': instance.focus || instance.overlayVisible + }), + pcInput: 'p-datepicker-input', + dropdown: 'p-datepicker-dropdown', + inputIconContainer: 'p-datepicker-input-icon-container', + inputIcon: 'p-datepicker-input-icon', + panel: ({ instance }) => ({ + 'p-datepicker-panel p-component': true, + 'p-datepicker-panel-inline': instance.inline, + 'p-disabled': instance.disabled, + 'p-datepicker-timeonly': instance.timeOnly + }), + calendarContainer: 'p-datepicker-calendar-container', + calendar: 'p-datepicker-calendar', + header: 'p-datepicker-header', + pcPrevButton: 'p-datepicker-prev-button', + title: 'p-datepicker-title', + selectMonth: 'p-datepicker-select-month', + selectYear: 'p-datepicker-select-year', + decade: 'p-datepicker-decade', + pcNextButton: 'p-datepicker-next-button', + dayView: 'p-datepicker-day-view', + weekHeader: 'p-datepicker-weekheader p-disabled', + weekNumber: 'p-datepicker-weeknumber', + weekLabelContainer: 'p-datepicker-weeklabel-container p-disabled', + weekDayCell: 'p-datepicker-weekday-cell', + weekDay: 'p-datepicker-weekday', + dayCell: ({ date }) => [ + 'p-datepicker-day-cell', + { + 'p-datepicker-other-month': date.otherMonth, + 'p-datepicker-today': date.today + } + ], + day: ({ instance, date }) => { + let selectedDayClass = ''; + + if (instance.isRangeSelection() && instance.isSelected(date) && date.selectable) { + selectedDayClass = date.day === instance.value[0].getDate() || date.day === instance.value[1].getDate() ? 'p-datepicker-day-selected' : 'p-datepicker-day-selected-range'; + } + + return { + 'p-datepicker-day': true, + 'p-datepicker-day-selected': !instance.isRangeSelection() && instance.isSelected(date) && date.selectable, + 'p-disabled': instance.disabled || !date.selectable, + selectedDayClass: true + }; + }, + monthView: 'p-datepicker-month-view', + month: ({ instance, props, month, index }) => [ + 'p-datepicker-month', + { + 'p-datepicker-month-selected': instance.isMonthSelected(index), + 'p-disabled': props.disabled || !month.selectable + } + ], + yearView: 'p-datepicker-year-view', + year: ({ instance, props, year }) => [ + 'p-datepicker-year', + { + 'p-datepicker-year-selected': instance.isYearSelected(year.value), + 'p-disabled': props.disabled || !year.selectable + } + ], + timePicker: 'p-datepicker-time-picker', + hourPicker: 'p-datepicker-hour-picker', + pcIncrementButton: 'p-datepicker-increment-button', + pcDecrementButton: 'p-datepicker-decrement-button', + separator: 'p-datepicker-separator', + minutePicker: 'p-datepicker-minute-picker', + secondPicker: 'p-datepicker-second-picker', + ampmPicker: 'p-datepicker-ampm-picker', + buttonbar: 'p-datepicker-buttonbar', + pcTodayButton: 'p-datepicker-today-button', + pcClearButton: 'p-datepicker-clear-button' +}; + +@Injectable() +export class DatePickerStyle extends BaseStyle { + name = 'datepicker'; + + theme = theme; + + classes = classes; + + inlineStyles = inlineStyles; +}