Accessibility for Calendar
parent
e25ec06753
commit
5adb1e8d78
|
@ -1,24 +1,25 @@
|
|||
<template>
|
||||
<span ref="container" :class="containerClass" :style="style">
|
||||
<input :ref="inputRef" v-if="!inline" type="text" :class="['p-inputtext p-component', inputClass]" :style="inputStyle" @input="onInput" v-bind="$attrs"
|
||||
@focus="onFocus" @blur="onBlur" @keydown="onKeyDown" :readonly="!manualInput" inputmode="none">
|
||||
<CalendarButton v-if="showIcon" :icon="icon" tabindex="-1" class="p-datepicker-trigger" :disabled="$attrs.disabled" @click="onButtonClick" type="button" :aria-label="inputFieldValue"/>
|
||||
<span ref="container" :id="id" :class="containerClass" :style="style">
|
||||
<input :ref="inputRef" v-if="!inline" type="text" role="combobox" :id="inputId" :class="['p-inputtext p-component', inputClass]" :style="inputStyle"
|
||||
aria-autocomplete="none" aria-haspopup="dialog" :aria-expanded="overlayVisible" :aria-controls="panelId" inputmode="none"
|
||||
@input="onInput" @focus="onFocus" @blur="onBlur" @keydown="onKeyDown" :readonly="!manualInput" v-bind="inputProps">
|
||||
<CalendarButton v-if="showIcon" :icon="icon" class="p-datepicker-trigger" :disabled="disabled" @click="onButtonClick" type="button" :aria-label="$primevue.config.locale.chooseDate" aria-haspopup="dialog" :aria-expanded="overlayVisible" :aria-controls="panelId"/>
|
||||
<Portal :appendTo="appendTo" :disabled="inline">
|
||||
<transition name="p-connected-overlay" @enter="onOverlayEnter($event)" @after-enter="onOverlayEnterComplete" @after-leave="onOverlayAfterLeave" @leave="onOverlayLeave">
|
||||
<div :ref="overlayRef" :class="panelStyleClass" v-if="inline || overlayVisible" :role="inline ? null : 'dialog'" @click="onOverlayClick" @mouseup="onOverlayMouseUp">
|
||||
<div :ref="overlayRef" :id="panelId" :class="panelStyleClass" v-if="inline || overlayVisible" :role="inline ? null : 'dialog'" :aria-modal="inline ? null : 'true'" :aria-label="$primevue.config.locale.chooseDate" @click="onOverlayClick" @mouseup="onOverlayMouseUp" v-bind="panelProps">
|
||||
<template v-if="!timeOnly">
|
||||
<div class="p-datepicker-group-container">
|
||||
<div class="p-datepicker-group" v-for="(month,groupIndex) of months" :key="month.month + month.year">
|
||||
<div class="p-datepicker-header">
|
||||
<slot name="header"></slot>
|
||||
<button class="p-datepicker-prev p-link" v-show="groupIndex === 0" @click="onPrevButtonClick" type="button" @keydown="onContainerButtonKeydown" v-ripple :disabled="$attrs.disabled">
|
||||
<button class="p-datepicker-prev p-link" v-show="groupIndex === 0" @click="onPrevButtonClick" type="button" @keydown="onContainerButtonKeydown" v-ripple :disabled="disabled" :aria-label=" currentView === 'year' ? $primevue.config.locale.prevDecade: currentView === 'month' ? $primevue.config.locale.prevYear : $primevue.config.locale.prevMonth">
|
||||
<span class="p-datepicker-prev-icon pi pi-chevron-left"></span>
|
||||
</button>
|
||||
<div class="p-datepicker-title">
|
||||
<button type="button" @click="switchToMonthView" @keydown="onContainerButtonKeydown" v-if="currentView === 'date'" class="p-datepicker-month p-link" :disabled="switchViewButtonDisabled">
|
||||
<button type="button" @click="switchToMonthView" @keydown="onContainerButtonKeydown" v-if="currentView === 'date'" class="p-datepicker-month p-link" :disabled="switchViewButtonDisabled" :aria-label="$primevue.config.locale.chooseMonth">
|
||||
{{getMonthName(month.month)}}
|
||||
</button>
|
||||
<button type="button" @click="switchToYearView" @keydown="onContainerButtonKeydown" v-if="currentView !== 'year'" class="p-datepicker-year p-link" :disabled="switchViewButtonDisabled">
|
||||
<button type="button" @click="switchToYearView" @keydown="onContainerButtonKeydown" v-if="currentView !== 'year'" class="p-datepicker-year p-link" :disabled="switchViewButtonDisabled" :aria-label="$primevue.config.locale.chooseYear">
|
||||
{{getYear(month)}}
|
||||
</button>
|
||||
<span class="p-datepicker-decade" v-if="currentView === 'year'">
|
||||
|
@ -28,18 +29,18 @@
|
|||
</span>
|
||||
</div>
|
||||
<button class="p-datepicker-next p-link" v-show="numberOfMonths === 1 ? true : (groupIndex === numberOfMonths - 1)"
|
||||
@click="onNextButtonClick" type="button" @keydown="onContainerButtonKeydown" v-ripple :disabled="$attrs.disabled">
|
||||
@click="onNextButtonClick" type="button" @keydown="onContainerButtonKeydown" v-ripple :disabled="disabled" :aria-label=" currentView === 'year' ? $primevue.config.locale.nextDecade : currentView === 'month' ? $primevue.config.locale.nextYear : $primevue.config.locale.nextMonth">
|
||||
<span class="p-datepicker-next-icon pi pi-chevron-right"></span>
|
||||
</button>
|
||||
</div>
|
||||
<div class="p-datepicker-calendar-container" v-if="currentView ==='date'">
|
||||
<table class="p-datepicker-calendar">
|
||||
<table class="p-datepicker-calendar" role="grid">
|
||||
<thead>
|
||||
<tr>
|
||||
<th scope="col" v-if="showWeek" class="p-datepicker-weekheader p-disabled">
|
||||
<span>{{weekHeaderLabel}}</span>
|
||||
</th>
|
||||
<th scope="col" v-for="weekDay of weekDays" :key="weekDay">
|
||||
<th scope="col" v-for="weekDay of weekDays" :key="weekDay" :abbr="weekDay">
|
||||
<span>{{weekDay}}</span>
|
||||
</th>
|
||||
</tr>
|
||||
|
@ -52,11 +53,14 @@
|
|||
{{month.weekNumbers[i]}}
|
||||
</span>
|
||||
</td>
|
||||
<td v-for="date of week" :key="date.day + '' + date.month" :class="{'p-datepicker-other-month': date.otherMonth, 'p-datepicker-today': date.today}">
|
||||
<td v-for="date of week" :aria-label="date.day" :key="date.day + '' + date.month" :class="{'p-datepicker-other-month': date.otherMonth, 'p-datepicker-today': date.today}">
|
||||
<span :class="{'p-highlight': isSelected(date), 'p-disabled': !date.selectable}" @click="onDateSelect($event, date)"
|
||||
draggable="false" @keydown="onDateCellKeydown($event,date,groupIndex)" v-ripple>
|
||||
draggable="false" @keydown="onDateCellKeydown($event,date,groupIndex)" v-ripple :aria-selected="isSelected(date)">
|
||||
<slot name="date" :date="date">{{date.day}}</slot>
|
||||
</span>
|
||||
<div v-if="isSelected(date)" class="p-hidden-accessible" aria-live="polite">
|
||||
{{date.day}}
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
|
@ -68,23 +72,29 @@
|
|||
<span v-for="(m,i) of monthPickerValues" :key="m" @click="onMonthSelect($event, i)" @keydown="onMonthCellKeydown($event,i)"
|
||||
class="p-monthpicker-month" :class="{'p-highlight': isMonthSelected(i)}" v-ripple>
|
||||
{{m}}
|
||||
<div v-if="isMonthSelected(i)" class="p-hidden-accessible" aria-live="polite">
|
||||
{{m}}
|
||||
</div>
|
||||
</span>
|
||||
</div>
|
||||
<div class="p-yearpicker" v-if="currentView === 'year'">
|
||||
<span v-for="y of yearPickerValues" :key="y" @click="onYearSelect($event, y)" @keydown="onYearCellKeydown($event,y)"
|
||||
class="p-yearpicker-year" :class="{'p-highlight': isYearSelected(y)}" v-ripple>
|
||||
{{y}}
|
||||
<div v-if="isYearSelected(y)" class="p-hidden-accessible" aria-live="polite">
|
||||
{{y}}
|
||||
</div>
|
||||
</span>
|
||||
</div>
|
||||
</template>
|
||||
<div class="p-timepicker" v-if="(showTime||timeOnly) && currentView === 'date'">
|
||||
<div class="p-hour-picker">
|
||||
<button class="p-link" @mousedown="onTimePickerElementMouseDown($event, 0, 1)" @mouseup="onTimePickerElementMouseUp($event)" @keydown="onContainerButtonKeydown" v-ripple
|
||||
<button class="p-link" :aria-label="$primevue.config.locale.nextHour" @mousedown="onTimePickerElementMouseDown($event, 0, 1)" @mouseup="onTimePickerElementMouseUp($event)" @keydown="onContainerButtonKeydown" v-ripple
|
||||
@mouseleave="onTimePickerElementMouseLeave()" @keydown.enter="onTimePickerElementMouseDown($event, 0, 1)" @keydown.space="onTimePickerElementMouseDown($event, 0, 1)" @keyup.enter="onTimePickerElementMouseUp($event)" @keyup.space="onTimePickerElementMouseUp($event)" type="button">
|
||||
<span class="pi pi-chevron-up"></span>
|
||||
</button>
|
||||
<span>{{formattedCurrentHour}}</span>
|
||||
<button class="p-link" @mousedown="onTimePickerElementMouseDown($event, 0, -1)" @mouseup="onTimePickerElementMouseUp($event)" @keydown="onContainerButtonKeydown" v-ripple
|
||||
<button class="p-link" :aria-label="$primevue.config.locale.prevHour" @mousedown="onTimePickerElementMouseDown($event, 0, -1)" @mouseup="onTimePickerElementMouseUp($event)" @keydown="onContainerButtonKeydown" v-ripple
|
||||
@mouseleave="onTimePickerElementMouseLeave()" @keydown.enter="onTimePickerElementMouseDown($event, 0, -1)" @keydown.space="onTimePickerElementMouseDown($event, 0, -1)" @keyup.enter="onTimePickerElementMouseUp($event)" @keyup.space="onTimePickerElementMouseUp($event)" type="button">
|
||||
<span class="pi pi-chevron-down"></span>
|
||||
</button>
|
||||
|
@ -93,12 +103,12 @@
|
|||
<span>{{timeSeparator}}</span>
|
||||
</div>
|
||||
<div class="p-minute-picker">
|
||||
<button class="p-link" @mousedown="onTimePickerElementMouseDown($event, 1, 1)" @mouseup="onTimePickerElementMouseUp($event)" @keydown="onContainerButtonKeydown" v-ripple :disabled="$attrs.disabled"
|
||||
<button class="p-link" :aria-label="$primevue.config.locale.nextMinute" @mousedown="onTimePickerElementMouseDown($event, 1, 1)" @mouseup="onTimePickerElementMouseUp($event)" @keydown="onContainerButtonKeydown" v-ripple :disabled="disabled"
|
||||
@mouseleave="onTimePickerElementMouseLeave()" @keydown.enter="onTimePickerElementMouseDown($event, 1, 1)" @keydown.space="onTimePickerElementMouseDown($event, 1, 1)" @keyup.enter="onTimePickerElementMouseUp($event)" @keyup.space="onTimePickerElementMouseUp($event)" type="button">
|
||||
<span class="pi pi-chevron-up"></span>
|
||||
</button>
|
||||
<span>{{formattedCurrentMinute}}</span>
|
||||
<button class="p-link" @mousedown="onTimePickerElementMouseDown($event, 1, -1)" @mouseup="onTimePickerElementMouseUp($event)" @keydown="onContainerButtonKeydown" v-ripple :disabled="$attrs.disabled"
|
||||
<button class="p-link" :aria-label="$primevue.config.locale.prevMinute" @mousedown="onTimePickerElementMouseDown($event, 1, -1)" @mouseup="onTimePickerElementMouseUp($event)" @keydown="onContainerButtonKeydown" v-ripple :disabled="disabled"
|
||||
@mouseleave="onTimePickerElementMouseLeave()" @keydown.enter="onTimePickerElementMouseDown($event, 1, -1)" @keydown.space="onTimePickerElementMouseDown($event, 1, -1)" @keyup.enter="onTimePickerElementMouseUp($event)" @keyup.space="onTimePickerElementMouseUp($event)" type="button">
|
||||
<span class="pi pi-chevron-down"></span>
|
||||
</button>
|
||||
|
@ -107,12 +117,12 @@
|
|||
<span>{{timeSeparator}}</span>
|
||||
</div>
|
||||
<div class="p-second-picker" v-if="showSeconds">
|
||||
<button class="p-link" @mousedown="onTimePickerElementMouseDown($event, 2, 1)" @mouseup="onTimePickerElementMouseUp($event)" @keydown="onContainerButtonKeydown" v-ripple :disabled="$attrs.disabled"
|
||||
<button class="p-link" :aria-label="$primevue.config.locale.nextSecond" @mousedown="onTimePickerElementMouseDown($event, 2, 1)" @mouseup="onTimePickerElementMouseUp($event)" @keydown="onContainerButtonKeydown" v-ripple :disabled="disabled"
|
||||
@mouseleave="onTimePickerElementMouseLeave()" @keydown.enter="onTimePickerElementMouseDown($event, 2, 1)" @keydown.space="onTimePickerElementMouseDown($event, 2, 1)" @keyup.enter="onTimePickerElementMouseUp($event)" @keyup.space="onTimePickerElementMouseUp($event)" type="button">
|
||||
<span class="pi pi-chevron-up"></span>
|
||||
</button>
|
||||
<span>{{formattedCurrentSecond}}</span>
|
||||
<button class="p-link" @mousedown="onTimePickerElementMouseDown($event, 2, -1)" @mouseup="onTimePickerElementMouseUp($event)" @keydown="onContainerButtonKeydown" v-ripple :disabled="$attrs.disabled"
|
||||
<button class="p-link" :aria-label="$primevue.config.locale.prevSecond" @mousedown="onTimePickerElementMouseDown($event, 2, -1)" @mouseup="onTimePickerElementMouseUp($event)" @keydown="onContainerButtonKeydown" v-ripple :disabled="disabled"
|
||||
@mouseleave="onTimePickerElementMouseLeave()" @keydown.enter="onTimePickerElementMouseDown($event, 2, -1)" @keydown.space="onTimePickerElementMouseDown($event, 2, -1)" @keyup.enter="onTimePickerElementMouseUp($event)" @keyup.space="onTimePickerElementMouseUp($event)" type="button">
|
||||
<span class="pi pi-chevron-down"></span>
|
||||
</button>
|
||||
|
@ -121,11 +131,11 @@
|
|||
<span>{{timeSeparator}}</span>
|
||||
</div>
|
||||
<div class="p-ampm-picker" v-if="hourFormat=='12'">
|
||||
<button class="p-link" @click="toggleAMPM($event)" type="button" v-ripple :disabled="$attrs.disabled">
|
||||
<button class="p-link" :aria-label="$primevue.config.locale.am" @click="toggleAMPM($event)" type="button" v-ripple :disabled="disabled">
|
||||
<span class="pi pi-chevron-up"></span>
|
||||
</button>
|
||||
<span>{{pm ? 'PM' : 'AM'}}</span>
|
||||
<button class="p-link" @click="toggleAMPM($event)" type="button" v-ripple :disabled="$attrs.disabled">
|
||||
<button class="p-link" :aria-label="$primevue.config.locale.pm" @click="toggleAMPM($event)" type="button" v-ripple :disabled="disabled">
|
||||
<span class="pi pi-chevron-down"></span>
|
||||
</button>
|
||||
</div>
|
||||
|
@ -150,7 +160,6 @@ import Portal from 'primevue/portal';
|
|||
|
||||
export default {
|
||||
name: 'Calendar',
|
||||
inheritAttrs: false,
|
||||
emits: ['show', 'hide', 'input', 'month-change', 'year-change', 'date-select', 'update:modelValue', 'today-click', 'clear-click', 'focus', 'blur', 'keydown'],
|
||||
props: {
|
||||
modelValue: null,
|
||||
|
@ -303,10 +312,22 @@ export default {
|
|||
type: String,
|
||||
default: 'body'
|
||||
},
|
||||
id: null,
|
||||
inputId: null,
|
||||
inputClass: null,
|
||||
inputStyle: null,
|
||||
class: null,
|
||||
style: null
|
||||
style: null,
|
||||
inputProps: null,
|
||||
panelProps: null,
|
||||
disabled: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
readonly: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
},
|
||||
navigationState: null,
|
||||
timePickerChange: false,
|
||||
|
@ -329,7 +350,7 @@ export default {
|
|||
if (this.inline) {
|
||||
this.overlay && this.overlay.setAttribute(this.attributeSelector, '');
|
||||
|
||||
if (!this.$attrs.disabled) {
|
||||
if (!this.disabled) {
|
||||
this.preventFocus = true;
|
||||
this.initFocusableCell();
|
||||
|
||||
|
@ -659,6 +680,10 @@ export default {
|
|||
else if (this.currentView === 'year') {
|
||||
this.decrementDecade();
|
||||
}
|
||||
else {
|
||||
if (event.shiftKey) {
|
||||
this.decrementYear();
|
||||
}
|
||||
else {
|
||||
if (this.currentMonth === 0) {
|
||||
this.currentMonth = 11;
|
||||
|
@ -670,6 +695,7 @@ export default {
|
|||
|
||||
this.$emit('month-change', {month: this.currentMonth + 1, year: this.currentYear});
|
||||
}
|
||||
}
|
||||
},
|
||||
navForward(event) {
|
||||
event.preventDefault();
|
||||
|
@ -684,6 +710,10 @@ export default {
|
|||
else if (this.currentView === 'year') {
|
||||
this.incrementDecade();
|
||||
}
|
||||
else {
|
||||
if (event.shiftKey) {
|
||||
this.incrementYear();
|
||||
}
|
||||
else {
|
||||
if (this.currentMonth === 11) {
|
||||
this.currentMonth = 0;
|
||||
|
@ -695,6 +725,7 @@ export default {
|
|||
|
||||
this.$emit('month-change', {month: this.currentMonth + 1, year: this.currentYear});
|
||||
}
|
||||
}
|
||||
},
|
||||
decrementYear() {
|
||||
this.currentYear--;
|
||||
|
@ -719,7 +750,7 @@ export default {
|
|||
event.preventDefault();
|
||||
},
|
||||
isEnabled() {
|
||||
return !this.$attrs.disabled && !this.$attrs.readonly;
|
||||
return !this.disabled && !this.readonly;
|
||||
},
|
||||
updateCurrentTimeMeta(date) {
|
||||
let currentHour = date.getHours();
|
||||
|
@ -853,7 +884,7 @@ export default {
|
|||
this.$emit('year-change', {month: this.currentMonth + 1, year: this.currentYear});
|
||||
},
|
||||
onDateSelect(event, dateMeta) {
|
||||
if (this.$attrs.disabled || !dateMeta.selectable) {
|
||||
if (this.disabled || !dateMeta.selectable) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -1736,9 +1767,8 @@ export default {
|
|||
const cellContent = event.currentTarget;
|
||||
const cell = cellContent.parentElement;
|
||||
|
||||
switch (event.which) {
|
||||
//down arrow
|
||||
case 40: {
|
||||
switch (event.code) {
|
||||
case 'ArrowDown': {
|
||||
cellContent.tabIndex = '-1';
|
||||
let cellIndex = DomHandler.index(cell);
|
||||
let nextRow = cell.parentElement.nextElementSibling;
|
||||
|
@ -1761,8 +1791,7 @@ export default {
|
|||
break;
|
||||
}
|
||||
|
||||
//up arrow
|
||||
case 38: {
|
||||
case 'ArrowUp': {
|
||||
cellContent.tabIndex = '-1';
|
||||
let cellIndex = DomHandler.index(cell);
|
||||
let prevRow = cell.parentElement.previousElementSibling;
|
||||
|
@ -1785,14 +1814,13 @@ export default {
|
|||
break;
|
||||
}
|
||||
|
||||
//left arrow
|
||||
case 37: {
|
||||
case 'ArrowLeft': {
|
||||
cellContent.tabIndex = '-1';
|
||||
let prevCell = cell.previousElementSibling;
|
||||
if (prevCell) {
|
||||
let focusCell = prevCell.children[0];
|
||||
if (DomHandler.hasClass(focusCell, 'p-disabled')) {
|
||||
this.navigateToMonth(true, groupIndex);
|
||||
this.navigateToMonth(event, true, groupIndex);
|
||||
}
|
||||
else {
|
||||
focusCell.tabIndex = '0';
|
||||
|
@ -1800,20 +1828,19 @@ export default {
|
|||
}
|
||||
}
|
||||
else {
|
||||
this.navigateToMonth(true, groupIndex);
|
||||
this.navigateToMonth(event, true, groupIndex);
|
||||
}
|
||||
event.preventDefault();
|
||||
break;
|
||||
}
|
||||
|
||||
//right arrow
|
||||
case 39: {
|
||||
case 'ArrowRight': {
|
||||
cellContent.tabIndex = '-1';
|
||||
let nextCell = cell.nextElementSibling;
|
||||
if (nextCell) {
|
||||
let focusCell = nextCell.children[0];
|
||||
if (DomHandler.hasClass(focusCell, 'p-disabled')) {
|
||||
this.navigateToMonth(false, groupIndex);
|
||||
this.navigateToMonth(event, false, groupIndex);
|
||||
}
|
||||
else {
|
||||
focusCell.tabIndex = '0';
|
||||
|
@ -1821,42 +1848,94 @@ export default {
|
|||
}
|
||||
}
|
||||
else {
|
||||
this.navigateToMonth(false, groupIndex);
|
||||
this.navigateToMonth(event, false, groupIndex);
|
||||
}
|
||||
event.preventDefault();
|
||||
break;
|
||||
}
|
||||
|
||||
//enter
|
||||
//space
|
||||
case 13:
|
||||
case 32: {
|
||||
case 'Enter':
|
||||
case 'Space': {
|
||||
this.onDateSelect(event, date);
|
||||
event.preventDefault();
|
||||
break;
|
||||
}
|
||||
|
||||
//escape
|
||||
case 27: {
|
||||
case 'Escape': {
|
||||
this.overlayVisible = false;
|
||||
event.preventDefault();
|
||||
break;
|
||||
}
|
||||
|
||||
//tab
|
||||
case 9: {
|
||||
case 'Tab': {
|
||||
if (!this.inline) {
|
||||
this.trapFocus(event);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case 'Home': {
|
||||
cellContent.tabIndex = '-1';
|
||||
let currentRow = cell.parentElement;
|
||||
let focusCell = currentRow.children[0].children[0];
|
||||
if (DomHandler.hasClass(focusCell, 'p-disabled')) {
|
||||
this.navigateToMonth(event, true, groupIndex);
|
||||
}
|
||||
else {
|
||||
focusCell.tabIndex = '0';
|
||||
focusCell.focus();
|
||||
}
|
||||
|
||||
event.preventDefault();
|
||||
break;
|
||||
}
|
||||
|
||||
case 'End': {
|
||||
cellContent.tabIndex = '-1';
|
||||
let currentRow = cell.parentElement;
|
||||
let focusCell = currentRow.children[currentRow.children.length -1].children[0];
|
||||
if (DomHandler.hasClass(focusCell, 'p-disabled')) {
|
||||
this.navigateToMonth(event, false, groupIndex);
|
||||
}
|
||||
else {
|
||||
focusCell.tabIndex = '0';
|
||||
focusCell.focus();
|
||||
}
|
||||
|
||||
event.preventDefault();
|
||||
break;
|
||||
}
|
||||
|
||||
case 'PageUp': {
|
||||
cellContent.tabIndex = '-1';
|
||||
if (event.shiftKey) {
|
||||
this.navigationState = {backward: true};
|
||||
this.navBackward(event);
|
||||
}
|
||||
else this.navigateToMonth(event, true, groupIndex);
|
||||
|
||||
event.preventDefault();
|
||||
break;
|
||||
}
|
||||
|
||||
case 'PageDown': {
|
||||
cellContent.tabIndex = '-1';
|
||||
if (event.shiftKey) {
|
||||
this.navigationState = {backward: false};
|
||||
this.navForward(event);
|
||||
}
|
||||
else this.navigateToMonth(event, false, groupIndex);
|
||||
|
||||
event.preventDefault();
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
//no op
|
||||
break;
|
||||
}
|
||||
},
|
||||
navigateToMonth(prev, groupIndex) {
|
||||
navigateToMonth(event, prev, groupIndex) {
|
||||
if (prev) {
|
||||
if (this.numberOfMonths === 1 || (groupIndex === 0)) {
|
||||
this.navigationState = {backward: true};
|
||||
|
@ -1886,14 +1965,13 @@ export default {
|
|||
onMonthCellKeydown(event, index) {
|
||||
const cell = event.currentTarget;
|
||||
|
||||
switch (event.which) {
|
||||
//arrows
|
||||
case 38:
|
||||
case 40: {
|
||||
switch (event.code) {
|
||||
case 'ArrowUp':
|
||||
case 'ArrowDown': {
|
||||
cell.tabIndex = '-1';
|
||||
var cells = cell.parentElement.children;
|
||||
var cellIndex = DomHandler.index(cell);
|
||||
let nextCell = cells[event.which === 40 ? cellIndex + 3 : cellIndex -3];
|
||||
let nextCell = cells[event.code === 'ArrowDown' ? cellIndex + 3 : cellIndex -3];
|
||||
if (nextCell) {
|
||||
nextCell.tabIndex = '0';
|
||||
nextCell.focus();
|
||||
|
@ -1902,8 +1980,7 @@ export default {
|
|||
break;
|
||||
}
|
||||
|
||||
//left arrow
|
||||
case 37: {
|
||||
case 'ArrowLeft': {
|
||||
cell.tabIndex = '-1';
|
||||
let prevCell = cell.previousElementSibling;
|
||||
if (prevCell) {
|
||||
|
@ -1918,8 +1995,7 @@ export default {
|
|||
break;
|
||||
}
|
||||
|
||||
//right arrow
|
||||
case 39: {
|
||||
case 'ArrowRight': {
|
||||
cell.tabIndex = '-1';
|
||||
let nextCell = cell.nextElementSibling;
|
||||
if (nextCell) {
|
||||
|
@ -1934,24 +2010,36 @@ export default {
|
|||
break;
|
||||
}
|
||||
|
||||
//enter
|
||||
//space
|
||||
case 13:
|
||||
case 32: {
|
||||
case 'PageUp': {
|
||||
if (event.shiftKey) return;
|
||||
this.navigationState = {backward: true};
|
||||
this.navBackward(event);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case 'PageDown': {
|
||||
if (event.shiftKey) return;
|
||||
this.navigationState = {backward: false};
|
||||
this.navForward(event);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case 'Enter':
|
||||
case 'Space': {
|
||||
this.onMonthSelect(event, index);
|
||||
event.preventDefault();
|
||||
break;
|
||||
}
|
||||
|
||||
//escape
|
||||
case 27: {
|
||||
case 'Escape': {
|
||||
this.overlayVisible = false;
|
||||
event.preventDefault();
|
||||
break;
|
||||
}
|
||||
|
||||
//tab
|
||||
case 9: {
|
||||
case 'Tab': {
|
||||
this.trapFocus(event);
|
||||
break;
|
||||
}
|
||||
|
@ -1964,14 +2052,13 @@ export default {
|
|||
onYearCellKeydown(event, index) {
|
||||
const cell = event.currentTarget;
|
||||
|
||||
switch (event.which) {
|
||||
//arrows
|
||||
case 38:
|
||||
case 40: {
|
||||
switch (event.code) {
|
||||
case 'ArrowUp':
|
||||
case 'ArrowDown': {
|
||||
cell.tabIndex = '-1';
|
||||
var cells = cell.parentElement.children;
|
||||
var cellIndex = DomHandler.index(cell);
|
||||
let nextCell = cells[event.which === 40 ? cellIndex + 2 : cellIndex - 2];
|
||||
let nextCell = cells[event.code === 'ArrowDown' ? cellIndex + 2 : cellIndex - 2];
|
||||
if (nextCell) {
|
||||
nextCell.tabIndex = '0';
|
||||
nextCell.focus();
|
||||
|
@ -1980,8 +2067,7 @@ export default {
|
|||
break;
|
||||
}
|
||||
|
||||
//left arrow
|
||||
case 37: {
|
||||
case 'ArrowLeft': {
|
||||
cell.tabIndex = '-1';
|
||||
let prevCell = cell.previousElementSibling;
|
||||
if (prevCell) {
|
||||
|
@ -1996,8 +2082,7 @@ export default {
|
|||
break;
|
||||
}
|
||||
|
||||
//right arrow
|
||||
case 39: {
|
||||
case 'ArrowRight': {
|
||||
cell.tabIndex = '-1';
|
||||
let nextCell = cell.nextElementSibling;
|
||||
if (nextCell) {
|
||||
|
@ -2012,24 +2097,36 @@ export default {
|
|||
break;
|
||||
}
|
||||
|
||||
//enter
|
||||
//space
|
||||
case 13:
|
||||
case 32: {
|
||||
case 'PageUp': {
|
||||
if (event.shiftKey) return;
|
||||
this.navigationState = {backward: true};
|
||||
this.navBackward(event);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case 'PageDown': {
|
||||
if (event.shiftKey) return;
|
||||
this.navigationState = {backward: false};
|
||||
this.navForward(event);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case 'Enter':
|
||||
case 'Space': {
|
||||
this.onYearSelect(event, index);
|
||||
event.preventDefault();
|
||||
break;
|
||||
}
|
||||
|
||||
//escape
|
||||
case 27: {
|
||||
case 'Escape': {
|
||||
this.overlayVisible = false;
|
||||
event.preventDefault();
|
||||
break;
|
||||
}
|
||||
|
||||
//tab
|
||||
case 9: {
|
||||
case 'Tab': {
|
||||
this.trapFocus(event);
|
||||
break;
|
||||
}
|
||||
|
@ -2156,14 +2253,12 @@ export default {
|
|||
}
|
||||
},
|
||||
onContainerButtonKeydown(event) {
|
||||
switch (event.which) {
|
||||
//tab
|
||||
case 9:
|
||||
switch (event.code) {
|
||||
case 'Tab':
|
||||
this.trapFocus(event);
|
||||
break;
|
||||
|
||||
//escape
|
||||
case 27:
|
||||
case 'Escape':
|
||||
this.overlayVisible = false;
|
||||
event.preventDefault();
|
||||
break;
|
||||
|
@ -2206,16 +2301,16 @@ export default {
|
|||
event.target.value = this.formatValue(this.modelValue);
|
||||
},
|
||||
onKeyDown(event) {
|
||||
if (event.keyCode === 40 && this.overlay) {
|
||||
if (event.code === 'ArrowDown' && this.overlay) {
|
||||
this.trapFocus(event);
|
||||
}
|
||||
else if (event.keyCode === 27) {
|
||||
else if (event.code === 'Escape') {
|
||||
if (this.overlayVisible) {
|
||||
this.overlayVisible = false;
|
||||
event.preventDefault();
|
||||
}
|
||||
}
|
||||
else if (event.keyCode === 9) {
|
||||
else if (event.code === 'Tab') {
|
||||
if (this.overlay) {
|
||||
DomHandler.getFocusableElements(this.overlay).forEach(el => el.tabIndex = '-1');
|
||||
}
|
||||
|
@ -2334,7 +2429,7 @@ export default {
|
|||
{
|
||||
'p-calendar-w-btn': this.showIcon,
|
||||
'p-calendar-timeonly': this.timeOnly,
|
||||
'p-calendar-disabled': this.$attrs.disabled,
|
||||
'p-calendar-disabled': this.disabled,
|
||||
'p-inputwrapper-filled': this.modelValue,
|
||||
'p-inputwrapper-focus': this.focused
|
||||
}
|
||||
|
@ -2343,7 +2438,7 @@ export default {
|
|||
panelStyleClass() {
|
||||
return ['p-datepicker p-component', this.panelClass, {
|
||||
'p-datepicker-inline': this.inline,
|
||||
'p-disabled': this.$attrs.disabled,
|
||||
'p-disabled': this.disabled,
|
||||
'p-datepicker-timeonly': this.timeOnly,
|
||||
'p-datepicker-multiple-month': this.numberOfMonths > 1,
|
||||
'p-datepicker-monthpicker': (this.currentView === 'month'),
|
||||
|
@ -2509,7 +2604,10 @@ export default {
|
|||
return UniqueComponentId();
|
||||
},
|
||||
switchViewButtonDisabled() {
|
||||
return this.numberOfMonths > 1 || this.$attrs.disabled;
|
||||
return this.numberOfMonths > 1 || this.disabled;
|
||||
},
|
||||
panelId() {
|
||||
return UniqueComponentId() + '_panel';
|
||||
}
|
||||
},
|
||||
components: {
|
||||
|
|
|
@ -36,6 +36,23 @@ const defaultOptions = {
|
|||
dayNamesMin: ["Su","Mo","Tu","We","Th","Fr","Sa"],
|
||||
monthNames: ["January","February","March","April","May","June","July","August","September","October","November","December"],
|
||||
monthNamesShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun","Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
|
||||
chooseYear: 'Choose Year',
|
||||
chooseMonth: 'Choose Month',
|
||||
chooseDate: 'Choose Date',
|
||||
prevDecade: 'Previous Decade',
|
||||
nextDecade: 'Next Decade',
|
||||
prevYear: 'Previous Year',
|
||||
nextYear: 'Next Year',
|
||||
prevMonth: 'Previous Month',
|
||||
nextMonth: 'Next Month',
|
||||
prevHour: 'Previous Hour',
|
||||
nextHour: 'Next Hour',
|
||||
prevMinute: 'Previous Minute',
|
||||
nextMinute: 'Next Minute',
|
||||
prevSecond: 'Previous Second',
|
||||
nextSecond: 'Next Second',
|
||||
am: 'am',
|
||||
pm: 'pm',
|
||||
today: 'Today',
|
||||
weekHeader: 'Wk',
|
||||
firstDayOfWeek: 0,
|
||||
|
|
|
@ -628,6 +628,191 @@ export default {
|
|||
</table>
|
||||
</div>
|
||||
|
||||
<h5>Accessibility</h5>
|
||||
<DevelopmentSection>
|
||||
<h6>Screen Reader</h6>
|
||||
<p>Value to describe the component can either be provided via <i>label</i> tag combined with <i>inputId</i> prop or using <i>aria-labelledby</i>, <i>aria-label</i> props. The input element has <i>combobox</i> role
|
||||
in addition to <i>aria-autocomplete</i> as "none", <i>aria-haspopup</i> as "dialog" and <i>aria-expanded</i> attributes. The relation between the input and the popup is created with <i>aria-controls</i> attribute that refers to the id of the popup.</p>
|
||||
<p>The optional calendar button requires includes <i>aria-haspopup</i>, <i>aria-expanded</i> for states along with <i>aria-controls</i> to define the relation between the popup and the button. The value to read is retrieved from the <i>chooseDate</i>
|
||||
key of the aria property from the <router-link to="/locale">locale</router-link> API. This label is also used for the <i>aria-label</i> of the popup as well. When there is a value selected, it is formatted and appended to the label to be able to notify users
|
||||
about the current value.</p>
|
||||
|
||||
<p>Popup has a <i>dialog</i> role along with <i>aria-modal</i> and <i>aria-label</i>. The navigation buttons at the header has an <i>aria-label</i> retrieved from the <i>prevYear</i>, <i>nextYear</i>, <i>prevMonth</i>, <i>nextMonth</i>,
|
||||
<i>prevDecade</i> and <i>nextDecade</i> keys of the locale aria API. Similarly month picker button uses the <i>chooseMonth</i> and year picker button uses the <i>chooseYear</i> keys.</p>
|
||||
|
||||
<p>Main date table uses <i>grid</i> role that contains th elements with <i>col</i> as the scope along with <i>abbr</i> tag resolving to the full name of the month. Each date cell has an <i>aria-label</i> referring to the full date value.
|
||||
Buttons at the footer utilize their readable labels as <i>aria-label</i> as well. Selected date also receives the <i>aria-selected</i> attribute.</p>
|
||||
|
||||
<p>Timepicker spinner buttons get their labels for <i>aria-label</i> from the aria locale API using the <i>prevHour</i>, <i>nextHour</i>, <i>prevMinute</i>, <i>nextMinute</i>, <i>prevSecond</i>, <i>nextSecond</i>, <i>am</i> and <i>pm</i> keys.</p>
|
||||
|
||||
<p>Calendar also includes a hidden section that is only available to screen readers with <i>aria-live</i> as "polite". This element is updated when the selected date changes to instruct the user about the current date selected.</p>
|
||||
|
||||
<pre v-code><code>
|
||||
<label for="date1">Date</label>
|
||||
<Calendar inputId="date1" />
|
||||
|
||||
<span id="date2">Date</span>
|
||||
<Calendar aria-labelledby="date2" />
|
||||
|
||||
<Calendar aria-label="Date" />
|
||||
|
||||
</code></pre>
|
||||
|
||||
<h6>Choose Date Button Keyboard Support</h6>
|
||||
<div class="doc-tablewrapper">
|
||||
<table class="doc-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Key</th>
|
||||
<th>Function</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td><i>space</i></td>
|
||||
<td>Opens popup and moves focus to the selected date, if there is none focuses on today.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><i>enter</i></td>
|
||||
<td>Opens popup and moves focus to the selected date, if there is none focuses on today.</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<h6>Popup Keyboard Support</h6>
|
||||
<div class="doc-tablewrapper">
|
||||
<table class="doc-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Key</th>
|
||||
<th>Function</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td><i>escape</i></td>
|
||||
<td>Closes the popup and moves focus to the input element.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><i>tab</i></td>
|
||||
<td>Moves focus to the next focusable element within the popup.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><i>shift</i> + <i>tab</i></td>
|
||||
<td>Moves focus to the next focusable element within the popup.</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<h6>Header Buttons Keyboard Support</h6>
|
||||
<div class="doc-tablewrapper">
|
||||
<table class="doc-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Key</th>
|
||||
<th>Function</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td><i>enter</i></td>
|
||||
<td>Triggers the button action.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><i>space</i></td>
|
||||
<td>Triggers the button action.</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<h6>Date Grid Keyboard Support</h6>
|
||||
<div class="doc-tablewrapper">
|
||||
<table class="doc-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Key</th>
|
||||
<th>Function</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td><i>enter</i></td>
|
||||
<td>Selects the date, closes the popup and moves focus to the input element.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><i>space</i></td>
|
||||
<td>Selects the date, closes the popup and moves focus to the input element.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><i>up arrow</i></td>
|
||||
<td>Moves focus to the same day of the previous week.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><i>down arrow</i></td>
|
||||
<td>Moves focus to the same day of the next week.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><i>right arrow</i></td>
|
||||
<td>Moves focus to the next day.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><i>left arrow</i></td>
|
||||
<td>Moves focus to the previous day.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><i>home</i></td>
|
||||
<td>Moves focus to the first day of the current week.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><i>end</i></td>
|
||||
<td>Moves focus to the last day of the current week.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><i>page up</i></td>
|
||||
<td>Changes the date to previous month in date picker mode. Moves to previous year in month picker mode and previous decade in year picker.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><i>shift</i> + <i>page up</i></td>
|
||||
<td>Changes the date to previous year in date picker mode. Has no effect in month or year picker</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><i>page down</i></td>
|
||||
<td>Changes the date to next month in date picker mode. Moves to next year in month picker mode and next decade in year picker.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><i>shift</i> + <i>page down</i></td>
|
||||
<td>Changes the date to next year in date picker mode. Has no effect in month or year picker</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<h6>Footer Buttons Keyboard Support</h6>
|
||||
<div class="doc-tablewrapper">
|
||||
<table class="doc-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Key</th>
|
||||
<th>Function</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td><i>enter</i></td>
|
||||
<td>Triggers the button action.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><i>space</i></td>
|
||||
<td>Triggers the button action.</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</DevelopmentSection>
|
||||
|
||||
<h5>Dependencies</h5>
|
||||
<p>None.</p>
|
||||
</AppDoc>
|
||||
|
|
Loading…
Reference in New Issue