From 86dec008d15732ae423eff7451e75e010ada7eba Mon Sep 17 00:00:00 2001 From: PronDmytro Date: Sat, 4 May 2024 20:47:18 +0200 Subject: [PATCH 1/3] improve calendar keyboard accessibility --- src/app/components/calendar/calendar.ts | 102 ++++++++++++++++++++---- 1 file changed, 87 insertions(+), 15 deletions(-) diff --git a/src/app/components/calendar/calendar.ts b/src/app/components/calendar/calendar.ts index 2c185a590b7..db01bd36ef8 100644 --- a/src/app/components/calendar/calendar.ts +++ b/src/app/components/calendar/calendar.ts @@ -243,6 +243,7 @@ export const CALENDAR_VALUE_ACCESSOR: any = { [ngClass]="{ 'p-highlight': isSelected(date) && date.selectable, 'p-disabled': !date.selectable }" (click)="onDateSelect($event, date)" draggable="false" + [attr.data-date]="formatDateKey(formatDateMetaToDate(date))" (keydown)="onDateCellKeydown($event, date, i)" pRipple > @@ -1143,6 +1144,8 @@ export class Calendar implements OnInit, OnDestroy, ControlValueAccessor { _defaultDate!: Date; + _focusKey: Nullable = null; + private window: Window; get locale() { @@ -1622,6 +1625,14 @@ export class Calendar implements OnInit, OnDestroy, ControlValueAccessor { return formattedValue; } + formatDateMetaToDate(dateMeta: any): Date { + return new Date(dateMeta.year, dateMeta.month, dateMeta.day); + } + + formatDateKey(date: Date): string { + return `${date.getFullYear()}-${date.getMonth()}-${date.getDate()}`; + } + setCurrentHourPM(hours: number) { if (this.hourFormat == '12') { this.pm = hours > 11; @@ -1642,7 +1653,7 @@ export class Calendar implements OnInit, OnDestroy, ControlValueAccessor { } selectDate(dateMeta: any) { - let date = new Date(dateMeta.year, dateMeta.month, dateMeta.day); + let date = this.formatDateMetaToDate(dateMeta); if (this.showTime) { if (this.hourFormat == '12') { @@ -1837,7 +1848,7 @@ export class Calendar implements OnInit, OnDestroy, ControlValueAccessor { isDateBetween(start: Date, end: Date, dateMeta: any) { let between: boolean = false; if (ObjectUtils.isDate(start) && ObjectUtils.isDate(end)) { - let date: Date = new Date(dateMeta.year, dateMeta.month, dateMeta.day); + let date: Date = this.formatDateMetaToDate(dateMeta); return start.getTime() <= date.getTime() && end.getTime() >= date.getTime(); } @@ -2054,10 +2065,10 @@ export class Calendar implements OnInit, OnDestroy, ControlValueAccessor { } } - onDateCellKeydown(event: any, date: Date, groupIndex: number) { + onDateCellKeydown(event: any, dateMeta: any, groupIndex: number) { const cellContent = event.currentTarget; const cell = cellContent.parentElement; - + const currentDate = this.formatDateMetaToDate(dateMeta); switch (event.which) { //down arrow case 40: { @@ -2145,7 +2156,7 @@ export class Calendar implements OnInit, OnDestroy, ControlValueAccessor { //space case 13: case 32: { - this.onDateSelect(event, date); + this.onDateSelect(event, dateMeta); event.preventDefault(); break; } @@ -2166,6 +2177,52 @@ export class Calendar implements OnInit, OnDestroy, ControlValueAccessor { break; } + // page up + case 33: { + cellContent.tabIndex = '-1'; + const dateToFocus = new Date(currentDate.getFullYear(), currentDate.getMonth() - 1, currentDate.getDate()); + const focusKey = this.formatDateKey(dateToFocus); + this.navigateToMonth(true, groupIndex, `span[data-date='${focusKey}']:not(.p-disabled):not(.p-ink)`); + event.preventDefault(); + break; + } + + // page down + case 34: { + cellContent.tabIndex = '-1'; + const dateToFocus = new Date(currentDate.getFullYear(), currentDate.getMonth() + 1, currentDate.getDate()); + const focusKey = this.formatDateKey(dateToFocus); + this.navigateToMonth(false, groupIndex, `span[data-date='${focusKey}']:not(.p-disabled):not(.p-ink)`); + event.preventDefault(); + break; + } + + //home + case 36: + cellContent.tabIndex = '-1'; + const firstDayDate = new Date(currentDate.getFullYear(), currentDate.getMonth(), 1); + const firstDayDateKey = this.formatDateKey(firstDayDate); + const firstDayCell = DomHandler.findSingle(cellContent.offsetParent, `span[data-date='${firstDayDateKey}']:not(.p-disabled):not(.p-ink)`); + if (firstDayCell) { + firstDayCell.tabIndex = '0'; + firstDayCell.focus(); + } + event.preventDefault(); + break; + + //end + case 35: + cellContent.tabIndex = '-1'; + const lastDayDate = new Date(currentDate.getFullYear(), currentDate.getMonth() + 1, 0); + const lastDayDateKey = this.formatDateKey(lastDayDate); + const lastDayCell = DomHandler.findSingle(cellContent.offsetParent, `span[data-date='${lastDayDateKey}']:not(.p-disabled):not(.p-ink)`); + if (lastDayDate) { + lastDayCell.tabIndex = '0'; + lastDayCell.focus(); + } + event.preventDefault(); + break; + default: //no op break; @@ -2333,27 +2390,41 @@ export class Calendar implements OnInit, OnDestroy, ControlValueAccessor { } } - navigateToMonth(prev: any, groupIndex: number) { + navigateToMonth(prev: boolean, groupIndex: number, focusKey?: string) { if (prev) { if (this.numberOfMonths === 1 || groupIndex === 0) { this.navigationState = { backward: true }; + this._focusKey = focusKey; this.navBackward(event); } else { let prevMonthContainer = this.contentViewChild.nativeElement.children[groupIndex - 1]; - let cells = DomHandler.find(prevMonthContainer, '.p-datepicker-calendar td span:not(.p-disabled):not(.p-ink)'); - let focusCell = cells[cells.length - 1]; - focusCell.tabIndex = '0'; - focusCell.focus(); + if (focusKey) { + const firstDayCell = DomHandler.findSingle(prevMonthContainer, focusKey); + firstDayCell.tabIndex = '0'; + firstDayCell.focus(); + } else { + let cells = DomHandler.find(prevMonthContainer, '.p-datepicker-calendar td span:not(.p-disabled):not(.p-ink)'); + let focusCell = cells[cells.length - 1]; + focusCell.tabIndex = '0'; + focusCell.focus(); + } } } else { if (this.numberOfMonths === 1 || groupIndex === this.numberOfMonths - 1) { this.navigationState = { backward: false }; + this._focusKey = focusKey; this.navForward(event); } else { let nextMonthContainer = this.contentViewChild.nativeElement.children[groupIndex + 1]; - let focusCell = DomHandler.findSingle(nextMonthContainer, '.p-datepicker-calendar td span:not(.p-disabled):not(.p-ink)'); - focusCell.tabIndex = '0'; - focusCell.focus(); + if (focusKey) { + const firstDayCell = DomHandler.findSingle(nextMonthContainer, focusKey); + firstDayCell.tabIndex = '0'; + firstDayCell.focus(); + } else { + let focusCell = DomHandler.findSingle(nextMonthContainer, '.p-datepicker-calendar td span:not(.p-disabled):not(.p-ink)'); + focusCell.tabIndex = '0'; + focusCell.focus(); + } } } } @@ -2376,7 +2447,7 @@ export class Calendar implements OnInit, OnDestroy, ControlValueAccessor { } else if (this.currentView === 'year') { cells = DomHandler.find(this.contentViewChild.nativeElement, '.p-yearpicker .p-yearpicker-year:not(.p-disabled)'); } else { - cells = DomHandler.find(this.contentViewChild.nativeElement, '.p-datepicker-calendar td span:not(.p-disabled):not(.p-ink)'); + cells = DomHandler.find(this.contentViewChild.nativeElement, this._focusKey || '.p-datepicker-calendar td span:not(.p-disabled):not(.p-ink)'); } if (cells && cells.length > 0) { @@ -2388,7 +2459,7 @@ export class Calendar implements OnInit, OnDestroy, ControlValueAccessor { } else if (this.currentView === 'year') { cell = DomHandler.findSingle(this.contentViewChild.nativeElement, '.p-yearpicker .p-yearpicker-year:not(.p-disabled)'); } else { - cell = DomHandler.findSingle(this.contentViewChild.nativeElement, '.p-datepicker-calendar td span:not(.p-disabled):not(.p-ink)'); + cell = DomHandler.findSingle(this.contentViewChild.nativeElement, this._focusKey || '.p-datepicker-calendar td span:not(.p-disabled):not(.p-ink)'); } } @@ -2399,6 +2470,7 @@ export class Calendar implements OnInit, OnDestroy, ControlValueAccessor { } this.navigationState = null; + this._focusKey = null; } else { this.initFocusableCell(); } From 1346fa2c776d29a1eb44fba3904037ab06714c08 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mehmet=20=C3=87etin?= <92744169+mehmetcetin01140@users.noreply.github.com> Date: Tue, 7 May 2024 12:48:06 +0300 Subject: [PATCH 2/3] Fixed #15472 -Tooltip | Tooltip-option tooltipEvent=focus does not work on p-button --- src/app/components/tooltip/tooltip.ts | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/src/app/components/tooltip/tooltip.ts b/src/app/components/tooltip/tooltip.ts index 043a70a7bc1..9cb2b3adb42 100755 --- a/src/app/components/tooltip/tooltip.ts +++ b/src/app/components/tooltip/tooltip.ts @@ -180,7 +180,11 @@ export class Tooltip implements AfterViewInit, OnDestroy { this.focusListener = this.onFocus.bind(this); this.blurListener = this.onBlur.bind(this); - let target = this.getTarget(this.el.nativeElement); + let target = this.el.nativeElement.querySelector('.p-component'); + if (!target) { + target = this.el.nativeElement; + } + target.addEventListener('focus', this.focusListener); target.addEventListener('blur', this.blurListener); } @@ -638,7 +642,11 @@ export class Tooltip implements AfterViewInit, OnDestroy { this.el.nativeElement.removeEventListener('mouseleave', this.mouseLeaveListener); this.el.nativeElement.removeEventListener('click', this.clickListener); } else if (this.getOption('tooltipEvent') === 'focus') { - let target = this.getTarget(this.el.nativeElement); + let target = this.el.nativeElement.querySelector('.p-component'); + + if (!target) { + target = this.el.nativeElement; + } target.removeEventListener('focus', this.focusListener); target.removeEventListener('blur', this.blurListener); @@ -703,3 +711,4 @@ export class Tooltip implements AfterViewInit, OnDestroy { declarations: [Tooltip] }) export class TooltipModule {} + From 11b851e46ea251c74ebeb07d7d48589599016e15 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mehmet=20=C3=87etin?= <92744169+mehmetcetin01140@users.noreply.github.com> Date: Thu, 9 May 2024 13:42:14 +0300 Subject: [PATCH 3/3] refactor --- src/app/components/tooltip/tooltip.ts | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/src/app/components/tooltip/tooltip.ts b/src/app/components/tooltip/tooltip.ts index 9cb2b3adb42..4f31569d393 100755 --- a/src/app/components/tooltip/tooltip.ts +++ b/src/app/components/tooltip/tooltip.ts @@ -169,20 +169,24 @@ export class Tooltip implements AfterViewInit, OnDestroy { ngAfterViewInit() { if (isPlatformBrowser(this.platformId)) { this.zone.runOutsideAngular(() => { - if (this.getOption('tooltipEvent') === 'hover') { + const tooltipEvent = this.getOption('tooltipEvent'); + + if (tooltipEvent === 'hover' || tooltipEvent === 'both') { this.mouseEnterListener = this.onMouseEnter.bind(this); this.mouseLeaveListener = this.onMouseLeave.bind(this); this.clickListener = this.onInputClick.bind(this); this.el.nativeElement.addEventListener('mouseenter', this.mouseEnterListener); this.el.nativeElement.addEventListener('click', this.clickListener); this.el.nativeElement.addEventListener('mouseleave', this.mouseLeaveListener); - } else if (this.getOption('tooltipEvent') === 'focus') { + } + if (tooltipEvent === 'focus' || tooltipEvent === 'both') { this.focusListener = this.onFocus.bind(this); this.blurListener = this.onBlur.bind(this); let target = this.el.nativeElement.querySelector('.p-component'); + if (!target) { - target = this.el.nativeElement; + target = this.getTarget(this.el.nativeElement); } target.addEventListener('focus', this.focusListener); @@ -637,21 +641,23 @@ export class Tooltip implements AfterViewInit, OnDestroy { } unbindEvents() { - if (this.getOption('tooltipEvent') === 'hover') { + const tooltipEvent = this.getOption('tooltipEvent'); + + if (tooltipEvent === 'hover' || tooltipEvent === 'both') { this.el.nativeElement.removeEventListener('mouseenter', this.mouseEnterListener); this.el.nativeElement.removeEventListener('mouseleave', this.mouseLeaveListener); this.el.nativeElement.removeEventListener('click', this.clickListener); - } else if (this.getOption('tooltipEvent') === 'focus') { + } + if (tooltipEvent === 'focus' || tooltipEvent === 'both') { let target = this.el.nativeElement.querySelector('.p-component'); if (!target) { - target = this.el.nativeElement; + target = this.getTarget(this.el.nativeElement); } target.removeEventListener('focus', this.focusListener); target.removeEventListener('blur', this.blurListener); } - this.unbindDocumentResizeListener(); } @@ -711,4 +717,3 @@ export class Tooltip implements AfterViewInit, OnDestroy { declarations: [Tooltip] }) export class TooltipModule {} -