Refactor #3965 - For Calendar

pull/3997/head
mertsincan 2023-05-19 11:32:44 +01:00
parent 9e1f1b5194
commit b13d188e4f
5 changed files with 643 additions and 505 deletions

View File

@ -2,6 +2,19 @@
import { ObjectUtils } from 'primevue/utils'; import { ObjectUtils } from 'primevue/utils';
import { mergeProps } from 'vue'; import { mergeProps } from 'vue';
const inlineStyles = {
hiddenAccessible: {
border: '0',
clip: 'rect(0 0 0 0)',
height: '1px',
margin: '-1px',
overflow: 'hidden',
padding: '0',
position: 'absolute',
width: '1px'
}
};
export default { export default {
name: 'BaseComponent', name: 'BaseComponent',
props: { props: {
@ -37,6 +50,19 @@ export default {
}, },
ptmo(obj = {}, key = '', params = {}) { ptmo(obj = {}, key = '', params = {}) {
return this.getPTValue(obj, key, params); return this.getPTValue(obj, key, params);
},
css(key = '', params = {}) {
return !this.isUnstyled ? ObjectUtils.getItemValue(this.getOption(this.$options.style && this.$options.style.classes, key), { instance: this, props: this.$props, state: this.$data, ...params }) : undefined;
},
style(key = '', when = true, params = {}) {
if (when) {
const self = ObjectUtils.getItemValue(this.getOption(this.$options.style && this.$options.style.inlineStyles, key), { instance: this, props: this.$props, state: this.$data, ...params });
const base = ObjectUtils.getItemValue(this.getOption(inlineStyles, key), { instance: this, props: this.$props, state: this.$data, ...params });
return [base, self];
}
return undefined;
} }
}, },
computed: { computed: {
@ -44,7 +70,7 @@ export default {
return ObjectUtils.getItemValue(this.getOption(this.$primevue.config.pt, this.$.type.name), this.defaultsParams); return ObjectUtils.getItemValue(this.getOption(this.$primevue.config.pt, this.$.type.name), this.defaultsParams);
}, },
defaultsParams() { defaultsParams() {
return { instance: this.$ }; return { instance: this };
}, },
isUnstyled() { isUnstyled() {
return this.unstyled !== undefined ? this.unstyled : this.$primevue.config.unstyled; return this.unstyled !== undefined ? this.unstyled : this.$primevue.config.unstyled;

View File

@ -0,0 +1,459 @@
<script>
import BaseComponent from 'primevue/basecomponent';
import { useStyle } from 'primevue/usestyle';
const styles = `
.p-calendar {
display: inline-flex;
max-width: 100%;
}
.p-calendar .p-inputtext {
flex: 1 1 auto;
width: 1%;
}
.p-calendar-w-btn .p-inputtext {
border-top-right-radius: 0;
border-bottom-right-radius: 0;
}
.p-calendar-w-btn .p-datepicker-trigger {
border-top-left-radius: 0;
border-bottom-left-radius: 0;
}
/* Fluid */
.p-fluid .p-calendar {
display: flex;
}
.p-fluid .p-calendar .p-inputtext {
width: 1%;
}
/* Datepicker */
.p-calendar .p-datepicker {
min-width: 100%;
}
.p-datepicker {
width: auto;
}
.p-datepicker-inline {
display: inline-block;
overflow-x: auto;
}
/* Header */
.p-datepicker-header {
display: flex;
align-items: center;
justify-content: space-between;
}
.p-datepicker-header .p-datepicker-title {
margin: 0 auto;
}
.p-datepicker-prev,
.p-datepicker-next {
cursor: pointer;
display: inline-flex;
justify-content: center;
align-items: center;
overflow: hidden;
position: relative;
}
/* Multiple Month DatePicker */
.p-datepicker-multiple-month .p-datepicker-group-container {
display: flex;
}
.p-datepicker-multiple-month .p-datepicker-group-container .p-datepicker-group {
flex: 1 1 auto;
}
/* DatePicker Table */
.p-datepicker table {
width: 100%;
border-collapse: collapse;
}
.p-datepicker td > span {
display: flex;
justify-content: center;
align-items: center;
cursor: pointer;
margin: 0 auto;
overflow: hidden;
position: relative;
}
/* Month Picker */
.p-monthpicker-month {
width: 33.3%;
display: inline-flex;
align-items: center;
justify-content: center;
cursor: pointer;
overflow: hidden;
position: relative;
}
/* Year Picker */
.p-yearpicker-year {
width: 50%;
display: inline-flex;
align-items: center;
justify-content: center;
cursor: pointer;
overflow: hidden;
position: relative;
}
/* Button Bar */
.p-datepicker-buttonbar {
display: flex;
justify-content: space-between;
align-items: center;
}
/* Time Picker */
.p-timepicker {
display: flex;
justify-content: center;
align-items: center;
}
.p-timepicker button {
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
overflow: hidden;
position: relative;
}
.p-timepicker > div {
display: flex;
align-items: center;
flex-direction: column;
}
/* Touch UI */
.p-datepicker-touch-ui,
.p-calendar .p-datepicker-touch-ui {
min-width: 80vw;
}
`;
const inlineStyles = {
root: ({ props }) => ({ position: props.appendTo === 'self' ? 'relative' : undefined })
};
const classes = {
root: ({ props, state }) => [
'p-calendar p-component p-inputwrapper',
{
'p-calendar-w-btn': props.showIcon,
'p-calendar-timeonly': props.timeOnly,
'p-calendar-disabled': props.disabled,
'p-inputwrapper-filled': props.modelValue,
'p-inputwrapper-focus': state.focused
}
],
input: 'p-inputtext p-component',
dropdownButton: 'p-datepicker-trigger',
panel: ({ instance, props, state }) => [
'p-datepicker p-component',
props.panelClass,
{
'p-datepicker-inline': props.inline,
'p-disabled': props.disabled,
'p-datepicker-timeonly': props.timeOnly,
'p-datepicker-multiple-month': props.numberOfMonths > 1,
'p-datepicker-monthpicker': state.currentView === 'month',
'p-datepicker-yearpicker': state.currentView === 'year',
'p-datepicker-touch-ui': props.touchUI,
'p-input-filled': instance.$primevue.config.inputStyle === 'filled',
'p-ripple-disabled': instance.$primevue.config.ripple === false
}
],
groupContainer: 'p-datepicker-group-container',
group: 'p-datepicker-group',
header: 'p-datepicker-header',
previousButton: 'p-datepicker-prev p-link',
previousIcon: 'p-datepicker-prev-icon',
title: 'p-datepicker-title',
monthTitle: 'p-datepicker-month p-link',
yearTitle: 'p-datepicker-year p-link',
decadeTitle: 'p-datepicker-decade',
nextButton: 'p-datepicker-next p-link',
nextIcon: 'p-datepicker-next-icon',
container: 'p-datepicker-calendar-container',
table: 'p-datepicker-calendar',
weekHeader: 'p-datepicker-weekheader p-disabled',
weekNumber: 'p-datepicker-weeknumber',
weekLabelContainer: 'p-disabled',
day: ({ date }) => [{ 'p-datepicker-other-month': date.otherMonth, 'p-datepicker-today': date.today }],
dayLabel: ({ instance, date }) => [{ 'p-highlight': instance.isSelected(date), 'p-disabled': !date.selectable }],
ariaSelectedDay: 'p-hidden-accessible',
monthPicker: 'p-monthpicker',
month: ({ instance, month, index }) => ['p-monthpicker-month', { 'p-highlight': instance.isMonthSelected(index), 'p-disabled': !month.selectable }],
ariaMonth: 'p-hidden-accessible',
yearPicker: 'p-yearpicker',
year: ({ instance, year }) => ['p-yearpicker-year', { 'p-highlight': instance.isYearSelected(year.value), 'p-disabled': !year.selectable }],
ariaYear: 'p-hidden-accessible',
timePicker: 'p-timepicker',
hourPicker: 'p-hour-picker',
incrementButton: 'p-link',
decrementButton: 'p-link',
separatorContainer: 'p-separator',
minutePicker: 'p-minute-picker',
incrementButton: 'p-link',
decrementButton: 'p-link',
secondPicker: 'p-second-picker',
ampmPicker: 'p-ampm-picker',
buttonbar: 'p-datepicker-buttonbar',
todayButton: 'p-button-text',
clearButton: 'p-button-text'
};
const { load: loadStyle, unload: unloadStyle } = useStyle(styles, { id: 'primevue_calendar_style', manual: true });
export default {
name: 'BaseCalendar',
extends: BaseComponent,
props: {
modelValue: null,
selectionMode: {
type: String,
default: 'single'
},
dateFormat: {
type: String,
default: null
},
inline: {
type: Boolean,
default: false
},
showOtherMonths: {
type: Boolean,
default: true
},
selectOtherMonths: {
type: Boolean,
default: false
},
showIcon: {
type: Boolean,
default: false
},
icon: {
type: String,
default: undefined
},
previousIcon: {
type: String,
default: undefined
},
nextIcon: {
type: String,
default: undefined
},
incrementIcon: {
type: String,
default: undefined
},
decrementIcon: {
type: String,
default: undefined
},
numberOfMonths: {
type: Number,
default: 1
},
responsiveOptions: Array,
view: {
type: String,
default: 'date'
},
touchUI: {
type: Boolean,
default: false
},
monthNavigator: {
type: Boolean,
default: false
},
yearNavigator: {
type: Boolean,
default: false
},
yearRange: {
type: String,
default: null
},
minDate: {
type: Date,
value: null
},
maxDate: {
type: Date,
value: null
},
disabledDates: {
type: Array,
value: null
},
disabledDays: {
type: Array,
value: null
},
maxDateCount: {
type: Number,
value: null
},
showOnFocus: {
type: Boolean,
default: true
},
autoZIndex: {
type: Boolean,
default: true
},
baseZIndex: {
type: Number,
default: 0
},
showButtonBar: {
type: Boolean,
default: false
},
shortYearCutoff: {
type: String,
default: '+10'
},
showTime: {
type: Boolean,
default: false
},
timeOnly: {
type: Boolean,
default: false
},
hourFormat: {
type: String,
default: '24'
},
stepHour: {
type: Number,
default: 1
},
stepMinute: {
type: Number,
default: 1
},
stepSecond: {
type: Number,
default: 1
},
showSeconds: {
type: Boolean,
default: false
},
hideOnDateTimeSelect: {
type: Boolean,
default: false
},
hideOnRangeSelection: {
type: Boolean,
default: false
},
timeSeparator: {
type: String,
default: ':'
},
showWeek: {
type: Boolean,
default: false
},
manualInput: {
type: Boolean,
default: true
},
appendTo: {
type: String,
default: 'body'
},
disabled: {
type: Boolean,
default: false
},
readonly: {
type: Boolean,
default: false
},
placeholder: {
type: String,
default: null
},
id: {
type: String,
default: null
},
inputId: {
type: String,
default: null
},
inputClass: {
type: [String, Object],
default: null
},
inputStyle: {
type: Object,
default: null
},
inputProps: {
type: null,
default: null
},
panelClass: {
type: [String, Object],
default: null
},
panelStyle: {
type: Object,
default: null
},
panelProps: {
type: null,
default: null
},
'aria-labelledby': {
type: String,
default: null
},
'aria-label': {
type: String,
default: null
}
},
style: {
inlineStyles,
classes
},
watch: {
isUnstyled: {
immediate: true,
handler(newValue) {
!newValue && loadStyle();
}
}
}
};
</script>

View File

@ -1,12 +1,12 @@
<template> <template>
<span ref="container" :id="id" :class="containerClass" v-bind="ptm('root')"> <span ref="container" :id="id" :class="css('root')" :style="style('root')" data-pc-name="calendar" data-pc-section="root" v-bind="ptm('root')">
<input <input
v-if="!inline" v-if="!inline"
:ref="inputRef" :ref="inputRef"
:id="inputId" :id="inputId"
type="text" type="text"
role="combobox" role="combobox"
:class="['p-inputtext p-component', inputClass]" :class="[css('input'), inputClass]"
:style="inputStyle" :style="inputStyle"
:placeholder="placeholder" :placeholder="placeholder"
autocomplete="off" autocomplete="off"
@ -29,7 +29,7 @@
/> />
<CalendarButton <CalendarButton
v-if="showIcon" v-if="showIcon"
class="p-datepicker-trigger" :class="css('dropdownButton')"
:disabled="disabled" :disabled="disabled"
@click="onButtonClick" @click="onButtonClick"
type="button" type="button"
@ -51,7 +51,7 @@
v-if="inline || overlayVisible" v-if="inline || overlayVisible"
:ref="overlayRef" :ref="overlayRef"
:id="panelId" :id="panelId"
:class="panelStyleClass" :class="css('panel')"
:style="panelStyle" :style="panelStyle"
:role="inline ? null : 'dialog'" :role="inline ? null : 'dialog'"
:aria-modal="inline ? null : 'true'" :aria-modal="inline ? null : 'true'"
@ -62,14 +62,15 @@
v-bind="{ ...panelProps, ...ptm('panel') }" v-bind="{ ...panelProps, ...ptm('panel') }"
> >
<template v-if="!timeOnly"> <template v-if="!timeOnly">
<div class="p-datepicker-group-container" v-bind="ptm('groupContainer')"> <div :class="css('groupContainer')" v-bind="ptm('groupContainer')">
<div v-for="(month, groupIndex) of months" :key="month.month + month.year" class="p-datepicker-group" v-bind="ptm('group')"> <div v-for="(month, groupIndex) of months" :key="month.month + month.year" :class="css('group')" v-bind="ptm('group')">
<div class="p-datepicker-header" v-bind="ptm('header')"> <div :class="css('header')" v-bind="ptm('header')">
<slot name="header"></slot> <slot name="header"></slot>
<button <button
v-show="showOtherMonths ? groupIndex === 0 : false" v-show="showOtherMonths ? groupIndex === 0 : false"
:ref="previousButtonRef"
v-ripple v-ripple
class="p-datepicker-prev p-link" :class="css('previousButton')"
@click="onPrevButtonClick" @click="onPrevButtonClick"
type="button" type="button"
@keydown="onContainerButtonKeydown" @keydown="onContainerButtonKeydown"
@ -78,16 +79,16 @@
v-bind="ptm('previousButton')" v-bind="ptm('previousButton')"
> >
<slot name="previousicon"> <slot name="previousicon">
<component :is="previousIcon ? 'span' : 'ChevronLeftIcon'" :class="['p-datepicker-prev-icon', previousIcon]" v-bind="ptm('previousIcon')" /> <component :is="previousIcon ? 'span' : 'ChevronLeftIcon'" :class="[css('previousIcon'), previousIcon]" v-bind="ptm('previousIcon')" />
</slot> </slot>
</button> </button>
<div class="p-datepicker-title" v-bind="ptm('title')"> <div :class="css('title')" v-bind="ptm('title')">
<button <button
v-if="currentView === 'date'" v-if="currentView === 'date'"
type="button" type="button"
@click="switchToMonthView" @click="switchToMonthView"
@keydown="onContainerButtonKeydown" @keydown="onContainerButtonKeydown"
class="p-datepicker-month p-link" :class="css('monthTitle')"
:disabled="switchViewButtonDisabled" :disabled="switchViewButtonDisabled"
:aria-label="$primevue.config.locale.chooseMonth" :aria-label="$primevue.config.locale.chooseMonth"
v-bind="ptm('monthTitle')" v-bind="ptm('monthTitle')"
@ -99,21 +100,22 @@
type="button" type="button"
@click="switchToYearView" @click="switchToYearView"
@keydown="onContainerButtonKeydown" @keydown="onContainerButtonKeydown"
class="p-datepicker-year p-link" :class="css('yearTitle')"
:disabled="switchViewButtonDisabled" :disabled="switchViewButtonDisabled"
:aria-label="$primevue.config.locale.chooseYear" :aria-label="$primevue.config.locale.chooseYear"
v-bind="ptm('yearTitle')" v-bind="ptm('yearTitle')"
> >
{{ getYear(month) }} {{ getYear(month) }}
</button> </button>
<span v-if="currentView === 'year'" class="p-datepicker-decade" v-bind="ptm('decadeTitle')"> <span v-if="currentView === 'year'" :class="css('decadeTitle')" v-bind="ptm('decadeTitle')">
<slot name="decade" :years="yearPickerValues"> {{ yearPickerValues[0].value }} - {{ yearPickerValues[yearPickerValues.length - 1].value }} </slot> <slot name="decade" :years="yearPickerValues"> {{ yearPickerValues[0].value }} - {{ yearPickerValues[yearPickerValues.length - 1].value }} </slot>
</span> </span>
</div> </div>
<button <button
v-show="showOtherMonths ? (numberOfMonths === 1 ? true : groupIndex === numberOfMonths - 1) : false" v-show="showOtherMonths ? (numberOfMonths === 1 ? true : groupIndex === numberOfMonths - 1) : false"
:ref="nextButtonRef"
v-ripple v-ripple
class="p-datepicker-next p-link" :class="css('nextButton')"
@click="onNextButtonClick" @click="onNextButtonClick"
type="button" type="button"
@keydown="onContainerButtonKeydown" @keydown="onContainerButtonKeydown"
@ -122,15 +124,15 @@
v-bind="ptm('nextButton')" v-bind="ptm('nextButton')"
> >
<slot name="nexticon"> <slot name="nexticon">
<component :is="nextIcon ? 'span' : 'ChevronRightIcon'" :class="['p-datepicker-next-icon', nextIcon]" v-bind="ptm('nextIcon')" /> <component :is="nextIcon ? 'span' : 'ChevronRightIcon'" :class="[css('nextIcon'), nextIcon]" v-bind="ptm('nextIcon')" />
</slot> </slot>
</button> </button>
</div> </div>
<div v-if="currentView === 'date'" class="p-datepicker-calendar-container" v-bind="ptm('container')"> <div v-if="currentView === 'date'" :class="css('container')" v-bind="ptm('container')">
<table class="p-datepicker-calendar" role="grid" v-bind="ptm('table')"> <table :class="css('table')" role="grid" v-bind="ptm('table')">
<thead v-bind="ptm('tableHeader')"> <thead v-bind="ptm('tableHeader')">
<tr v-bind="ptm('tableHeaderRow')"> <tr v-bind="ptm('tableHeaderRow')">
<th v-if="showWeek" scope="col" class="p-datepicker-weekheader p-disabled" v-bind="ptm('weekHeader')"> <th v-if="showWeek" scope="col" :class="css('weekHeader')" :data-p-disabled="true" v-bind="ptm('weekHeader')">
<span v-bind="ptm('weekLabel')">{{ weekHeaderLabel }}</span> <span v-bind="ptm('weekLabel')">{{ weekHeaderLabel }}</span>
</th> </th>
<th v-for="weekDay of weekDays" :key="weekDay" scope="col" :abbr="weekDay" v-bind="ptm('tableHeaderCell')"> <th v-for="weekDay of weekDays" :key="weekDay" scope="col" :abbr="weekDay" v-bind="ptm('tableHeaderCell')">
@ -140,25 +142,28 @@
</thead> </thead>
<tbody v-bind="ptm('tableBody')"> <tbody v-bind="ptm('tableBody')">
<tr v-for="(week, i) of month.dates" :key="week[0].day + '' + week[0].month" v-bind="ptm('tableBodyRow')"> <tr v-for="(week, i) of month.dates" :key="week[0].day + '' + week[0].month" v-bind="ptm('tableBodyRow')">
<td v-if="showWeek" class="p-datepicker-weeknumber" v-bind="ptm('weekNumber')"> <td v-if="showWeek" :class="css('weekNumber')" v-bind="ptm('weekNumber')">
<span class="p-disabled" v-bind="ptm('weekLabelContainer')"> <span :class="css('weekLabelContainer')" :data-p-disabled="true" v-bind="ptm('weekLabelContainer')">
<span v-if="month.weekNumbers[i] < 10" style="visibility: hidden" v-bind="ptm('weekLabel')">0</span> <span v-if="month.weekNumbers[i] < 10" style="visibility: hidden" v-bind="ptm('weekLabel')">0</span>
{{ month.weekNumbers[i] }} {{ month.weekNumbers[i] }}
</span> </span>
</td> </td>
<td v-for="date of week" :key="date.day + '' + date.month" :aria-label="date.day" :class="{ 'p-datepicker-other-month': date.otherMonth, 'p-datepicker-today': date.today }" v-bind="ptm('day')"> <td v-for="date of week" :key="date.day + '' + date.month" :aria-label="date.day" :class="css('day', { date })" :data-p-today="date.today" :data-p-other-month="date.otherMonth" v-bind="ptm('day')">
<span <span
v-ripple v-ripple
:class="{ 'p-highlight': isSelected(date), 'p-disabled': !date.selectable }" :class="css('dayLabel', { date })"
@click="onDateSelect($event, date)" @click="onDateSelect($event, date)"
draggable="false" draggable="false"
@keydown="onDateCellKeydown($event, date, groupIndex)" @keydown="onDateCellKeydown($event, date, groupIndex)"
:aria-selected="isSelected(date)" :aria-selected="isSelected(date)"
:aria-disabled="!date.selectable"
:data-p-disabled="!date.selectable"
:data-p-highlight="isSelected(date)"
v-bind="ptm('dayLabel')" v-bind="ptm('dayLabel')"
> >
<slot name="date" :date="date">{{ date.day }}</slot> <slot name="date" :date="date">{{ date.day }}</slot>
</span> </span>
<div v-if="isSelected(date)" class="p-hidden-accessible" aria-live="polite" v-bind="ptm('ariaSelectedDay')"> <div v-if="isSelected(date)" :class="css('ariaSelectedDay')" :style="style('hiddenAccessible', isUnstyled)" aria-live="polite" :data-p-hidden-accessible="true" v-bind="ptm('ariaSelectedDay')">
{{ date.day }} {{ date.day }}
</div> </div>
</td> </td>
@ -168,46 +173,50 @@
</div> </div>
</div> </div>
</div> </div>
<div v-if="currentView === 'month'" class="p-monthpicker" v-bind="ptm('monthPicker')"> <div v-if="currentView === 'month'" :class="css('monthPicker')" data-pc-section="monthpicker" v-bind="ptm('monthPicker')">
<span <span
v-for="(m, i) of monthPickerValues" v-for="(m, i) of monthPickerValues"
:key="m" :key="m"
v-ripple v-ripple
@click="onMonthSelect($event, { month: m, index: i })" @click="onMonthSelect($event, { month: m, index: i })"
@keydown="onMonthCellKeydown($event, { month: m, index: i })" @keydown="onMonthCellKeydown($event, { month: m, index: i })"
class="p-monthpicker-month" :class="css('month', { month: m, index: i })"
:class="{ 'p-highlight': isMonthSelected(i), 'p-disabled': !m.selectable }" data-pc-section="month"
:data-p-disabled="!m.selectable"
:data-p-highlight="isMonthSelected(i)"
v-bind="ptm('month')" v-bind="ptm('month')"
> >
{{ m.value }} {{ m.value }}
<div v-if="isMonthSelected(i)" class="p-hidden-accessible" aria-live="polite" v-bind="ptm('ariaMonth')"> <div v-if="isMonthSelected(i)" :class="css('ariaMonth')" :style="style('hiddenAccessible', isUnstyled)" aria-live="polite" :data-p-hidden-accessible="true" v-bind="ptm('ariaMonth')">
{{ m.value }} {{ m.value }}
</div> </div>
</span> </span>
</div> </div>
<div v-if="currentView === 'year'" class="p-yearpicker" v-bind="ptm('yearPicker')"> <div v-if="currentView === 'year'" :class="css('yearPicker')" data-pc-section="yearpicker" v-bind="ptm('yearPicker')">
<span <span
v-for="y of yearPickerValues" v-for="y of yearPickerValues"
:key="y.value" :key="y.value"
v-ripple v-ripple
@click="onYearSelect($event, y)" @click="onYearSelect($event, y)"
@keydown="onYearCellKeydown($event, y)" @keydown="onYearCellKeydown($event, y)"
class="p-yearpicker-year" :class="css('year', { year: y })"
:class="{ 'p-highlight': isYearSelected(y.value), 'p-disabled': !y.selectable }" data-pc-section="year"
:data-p-disabled="!y.selectable"
:data-p-highlight="isYearSelected(y.value)"
v-bind="ptm('year')" v-bind="ptm('year')"
> >
{{ y.value }} {{ y.value }}
<div v-if="isYearSelected(y.value)" class="p-hidden-accessible" aria-live="polite" v-bind="ptm('ariaYear')"> <div v-if="isYearSelected(y.value)" :class="css('ariaYear')" :style="style('hiddenAccessible', isUnstyled)" aria-live="polite" :data-p-hidden-accessible="true" v-bind="ptm('ariaYear')">
{{ y.value }} {{ y.value }}
</div> </div>
</span> </span>
</div> </div>
</template> </template>
<div v-if="(showTime || timeOnly) && currentView === 'date'" class="p-timepicker" v-bind="ptm('timePicker')"> <div v-if="(showTime || timeOnly) && currentView === 'date'" :class="css('timePicker')" v-bind="ptm('timePicker')">
<div class="p-hour-picker" v-bind="ptm('hourPicker')"> <div :class="css('hourPicker')" v-bind="ptm('hourPicker')">
<button <button
v-ripple v-ripple
class="p-link" :class="css('incrementButton')"
:aria-label="$primevue.config.locale.nextHour" :aria-label="$primevue.config.locale.nextHour"
@mousedown="onTimePickerElementMouseDown($event, 0, 1)" @mousedown="onTimePickerElementMouseDown($event, 0, 1)"
@mouseup="onTimePickerElementMouseUp($event)" @mouseup="onTimePickerElementMouseUp($event)"
@ -227,7 +236,7 @@
<span v-bind="ptm('hour')">{{ formattedCurrentHour }}</span> <span v-bind="ptm('hour')">{{ formattedCurrentHour }}</span>
<button <button
v-ripple v-ripple
class="p-link" :class="css('decrementButton')"
:aria-label="$primevue.config.locale.prevHour" :aria-label="$primevue.config.locale.prevHour"
@mousedown="onTimePickerElementMouseDown($event, 0, -1)" @mousedown="onTimePickerElementMouseDown($event, 0, -1)"
@mouseup="onTimePickerElementMouseUp($event)" @mouseup="onTimePickerElementMouseUp($event)"
@ -245,13 +254,13 @@
</slot> </slot>
</button> </button>
</div> </div>
<div class="p-separator" v-bind="ptm('separatorContainer')"> <div :class="css('separatorContainer')" v-bind="ptm('separatorContainer')">
<span v-bind="ptm('separator')">{{ timeSeparator }}</span> <span v-bind="ptm('separator')">{{ timeSeparator }}</span>
</div> </div>
<div class="p-minute-picker" v-bind="ptm('minutePicker')"> <div :class="css('minutePicker')" v-bind="ptm('minutePicker')">
<button <button
v-ripple v-ripple
class="p-link" :class="css('incrementButton')"
:aria-label="$primevue.config.locale.nextMinute" :aria-label="$primevue.config.locale.nextMinute"
@mousedown="onTimePickerElementMouseDown($event, 1, 1)" @mousedown="onTimePickerElementMouseDown($event, 1, 1)"
@mouseup="onTimePickerElementMouseUp($event)" @mouseup="onTimePickerElementMouseUp($event)"
@ -272,7 +281,7 @@
<span v-bind="ptm('minute')">{{ formattedCurrentMinute }}</span> <span v-bind="ptm('minute')">{{ formattedCurrentMinute }}</span>
<button <button
v-ripple v-ripple
class="p-link" :class="css('decrementButton')"
:aria-label="$primevue.config.locale.prevMinute" :aria-label="$primevue.config.locale.prevMinute"
@mousedown="onTimePickerElementMouseDown($event, 1, -1)" @mousedown="onTimePickerElementMouseDown($event, 1, -1)"
@mouseup="onTimePickerElementMouseUp($event)" @mouseup="onTimePickerElementMouseUp($event)"
@ -291,13 +300,13 @@
</slot> </slot>
</button> </button>
</div> </div>
<div v-if="showSeconds" class="p-separator" v-bind="ptm('separatorContainer')"> <div v-if="showSeconds" :class="css('separatorContainer')" v-bind="ptm('separatorContainer')">
<span v-bind="ptm('separator')">{{ timeSeparator }}</span> <span v-bind="ptm('separator')">{{ timeSeparator }}</span>
</div> </div>
<div v-if="showSeconds" class="p-second-picker" v-bind="ptm('secondPicker')"> <div v-if="showSeconds" :class="css('secondPicker')" v-bind="ptm('secondPicker')">
<button <button
v-ripple v-ripple
class="p-link" :class="css('incrementButton')"
:aria-label="$primevue.config.locale.nextSecond" :aria-label="$primevue.config.locale.nextSecond"
@mousedown="onTimePickerElementMouseDown($event, 2, 1)" @mousedown="onTimePickerElementMouseDown($event, 2, 1)"
@mouseup="onTimePickerElementMouseUp($event)" @mouseup="onTimePickerElementMouseUp($event)"
@ -318,7 +327,7 @@
<span v-bind="ptm('second')">{{ formattedCurrentSecond }}</span> <span v-bind="ptm('second')">{{ formattedCurrentSecond }}</span>
<button <button
v-ripple v-ripple
class="p-link" :class="css('decrementButton')"
:aria-label="$primevue.config.locale.prevSecond" :aria-label="$primevue.config.locale.prevSecond"
@mousedown="onTimePickerElementMouseDown($event, 2, -1)" @mousedown="onTimePickerElementMouseDown($event, 2, -1)"
@mouseup="onTimePickerElementMouseUp($event)" @mouseup="onTimePickerElementMouseUp($event)"
@ -337,26 +346,26 @@
</slot> </slot>
</button> </button>
</div> </div>
<div v-if="hourFormat == '12'" class="p-separator" v-bind="ptm('separatorContainer')"> <div v-if="hourFormat == '12'" :class="css('separatorContainer')" v-bind="ptm('separatorContainer')">
<span v-bind="ptm('separator')">{{ timeSeparator }}</span> <span v-bind="ptm('separator')">{{ timeSeparator }}</span>
</div> </div>
<div v-if="hourFormat == '12'" class="p-ampm-picker" v-bind="ptm('ampmPicker')"> <div v-if="hourFormat == '12'" :class="css('ampmPicker')" v-bind="ptm('ampmPicker')">
<button v-ripple class="p-link" :aria-label="$primevue.config.locale.am" @click="toggleAMPM($event)" type="button" :disabled="disabled" v-bind="ptm('incrementButton')"> <button v-ripple :class="css('incrementButton')" :aria-label="$primevue.config.locale.am" @click="toggleAMPM($event)" type="button" :disabled="disabled" v-bind="ptm('incrementButton')">
<slot name="incrementicon"> <slot name="incrementicon">
<component :is="incrementIcon ? 'span' : 'ChevronUpIcon'" :class="incrementIcon" v-bind="ptm('incrementIcon')" /> <component :is="incrementIcon ? 'span' : 'ChevronUpIcon'" :class="css('incrementIcon')" v-bind="ptm('incrementIcon')" />
</slot> </slot>
</button> </button>
<span v-bind="ptm('ampm')">{{ pm ? $primevue.config.locale.pm : $primevue.config.locale.am }}</span> <span v-bind="ptm('ampm')">{{ pm ? $primevue.config.locale.pm : $primevue.config.locale.am }}</span>
<button v-ripple class="p-link" :aria-label="$primevue.config.locale.pm" @click="toggleAMPM($event)" type="button" :disabled="disabled" v-bind="ptm('decrementButton')"> <button v-ripple :class="css('decrementButton')" :aria-label="$primevue.config.locale.pm" @click="toggleAMPM($event)" type="button" :disabled="disabled" v-bind="ptm('decrementButton')">
<slot name="decrementicon"> <slot name="decrementicon">
<component :is="decrementIcon ? 'span' : 'ChevronDownIcon'" :class="decrementIcon" v-bind="ptm('decrementIcon')" /> <component :is="decrementIcon ? 'span' : 'ChevronDownIcon'" :class="css('decrementIcon')" v-bind="ptm('decrementIcon')" />
</slot> </slot>
</button> </button>
</div> </div>
</div> </div>
<div v-if="showButtonBar" class="p-datepicker-buttonbar" v-bind="ptm('buttonbar')"> <div v-if="showButtonBar" :class="css('buttonbar')" v-bind="ptm('buttonbar')">
<CalendarButton type="button" :label="todayLabel" @click="onTodayButtonClick($event)" class="p-button-text" @keydown="onContainerButtonKeydown" :pt="ptm('todayButton')" /> <CalendarButton type="button" :label="todayLabel" @click="onTodayButtonClick($event)" :class="css('todayButton')" @keydown="onContainerButtonKeydown" :pt="ptm('todayButton')" />
<CalendarButton type="button" :label="clearLabel" @click="onClearButtonClick($event)" class="p-button-text" @keydown="onContainerButtonKeydown" :pt="ptm('clearButton')" /> <CalendarButton type="button" :label="clearLabel" @click="onClearButtonClick($event)" :class="css('clearButton')" @keydown="onContainerButtonKeydown" :pt="ptm('clearButton')" />
</div> </div>
<slot name="footer"></slot> <slot name="footer"></slot>
</div> </div>
@ -366,7 +375,6 @@
</template> </template>
<script> <script>
import BaseComponent from 'primevue/basecomponent';
import Button from 'primevue/button'; import Button from 'primevue/button';
import CalendarIcon from 'primevue/icons/calendar'; import CalendarIcon from 'primevue/icons/calendar';
import ChevronDownIcon from 'primevue/icons/chevrondown'; import ChevronDownIcon from 'primevue/icons/chevrondown';
@ -377,227 +385,12 @@ import OverlayEventBus from 'primevue/overlayeventbus';
import Portal from 'primevue/portal'; import Portal from 'primevue/portal';
import Ripple from 'primevue/ripple'; import Ripple from 'primevue/ripple';
import { ConnectedOverlayScrollHandler, DomHandler, UniqueComponentId, ZIndexUtils } from 'primevue/utils'; import { ConnectedOverlayScrollHandler, DomHandler, UniqueComponentId, ZIndexUtils } from 'primevue/utils';
import BaseCalendar from './BaseCalendar';
export default { export default {
name: 'Calendar', name: 'Calendar',
extends: BaseComponent, extends: BaseCalendar,
emits: ['show', 'hide', 'input', 'month-change', 'year-change', 'date-select', 'update:modelValue', 'today-click', 'clear-click', 'focus', 'blur', 'keydown'], emits: ['show', 'hide', 'input', 'month-change', 'year-change', 'date-select', 'update:modelValue', 'today-click', 'clear-click', 'focus', 'blur', 'keydown'],
props: {
modelValue: null,
selectionMode: {
type: String,
default: 'single'
},
dateFormat: {
type: String,
default: null
},
inline: {
type: Boolean,
default: false
},
showOtherMonths: {
type: Boolean,
default: true
},
selectOtherMonths: {
type: Boolean,
default: false
},
showIcon: {
type: Boolean,
default: false
},
icon: {
type: String,
default: undefined
},
previousIcon: {
type: String,
default: undefined
},
nextIcon: {
type: String,
default: undefined
},
incrementIcon: {
type: String,
default: undefined
},
decrementIcon: {
type: String,
default: undefined
},
numberOfMonths: {
type: Number,
default: 1
},
responsiveOptions: Array,
view: {
type: String,
default: 'date'
},
touchUI: {
type: Boolean,
default: false
},
monthNavigator: {
type: Boolean,
default: false
},
yearNavigator: {
type: Boolean,
default: false
},
yearRange: {
type: String,
default: null
},
minDate: {
type: Date,
value: null
},
maxDate: {
type: Date,
value: null
},
disabledDates: {
type: Array,
value: null
},
disabledDays: {
type: Array,
value: null
},
maxDateCount: {
type: Number,
value: null
},
showOnFocus: {
type: Boolean,
default: true
},
autoZIndex: {
type: Boolean,
default: true
},
baseZIndex: {
type: Number,
default: 0
},
showButtonBar: {
type: Boolean,
default: false
},
shortYearCutoff: {
type: String,
default: '+10'
},
showTime: {
type: Boolean,
default: false
},
timeOnly: {
type: Boolean,
default: false
},
hourFormat: {
type: String,
default: '24'
},
stepHour: {
type: Number,
default: 1
},
stepMinute: {
type: Number,
default: 1
},
stepSecond: {
type: Number,
default: 1
},
showSeconds: {
type: Boolean,
default: false
},
hideOnDateTimeSelect: {
type: Boolean,
default: false
},
hideOnRangeSelection: {
type: Boolean,
default: false
},
timeSeparator: {
type: String,
default: ':'
},
showWeek: {
type: Boolean,
default: false
},
manualInput: {
type: Boolean,
default: true
},
appendTo: {
type: String,
default: 'body'
},
disabled: {
type: Boolean,
default: false
},
readonly: {
type: Boolean,
default: false
},
placeholder: {
type: String,
default: null
},
id: {
type: String,
default: null
},
inputId: {
type: String,
default: null
},
inputClass: {
type: [String, Object],
default: null
},
inputStyle: {
type: Object,
default: null
},
inputProps: {
type: null,
default: null
},
panelClass: {
type: [String, Object],
default: null
},
panelStyle: {
type: Object,
default: null
},
panelProps: {
type: null,
default: null
},
'aria-labelledby': {
type: String,
default: null
},
'aria-label': {
type: String,
default: null
}
},
navigationState: null, navigationState: null,
timePickerChange: false, timePickerChange: false,
scrollHandler: null, scrollHandler: null,
@ -607,6 +400,8 @@ export default {
overlay: null, overlay: null,
input: null, input: null,
mask: null, mask: null,
previousButton: null,
nextButton: null,
timePickerTimer: null, timePickerTimer: null,
preventFocus: false, preventFocus: false,
typeUpdate: false, typeUpdate: false,
@ -892,6 +687,9 @@ export default {
}, },
onOverlayEnter(el) { onOverlayEnter(el) {
el.setAttribute(this.attributeSelector, ''); el.setAttribute(this.attributeSelector, '');
const styles = this.touchUI ? { position: 'fixed', top: '50%', left: '50%', transform: 'translate(-50%, -50%)' } : !this.inline ? { position: 'absolute', top: '0', left: '0' } : undefined;
DomHandler.addStyles(el, styles);
if (this.autoZIndex) { if (this.autoZIndex) {
if (this.touchUI) ZIndexUtils.set('modal', el, this.baseZIndex || this.$primevue.config.zIndex.modal); if (this.touchUI) ZIndexUtils.set('modal', el, this.baseZIndex || this.$primevue.config.zIndex.modal);
@ -1083,12 +881,7 @@ export default {
return !(this.$el.isSameNode(event.target) || this.isNavIconClicked(event) || this.$el.contains(event.target) || (this.overlay && this.overlay.contains(event.target))); return !(this.$el.isSameNode(event.target) || this.isNavIconClicked(event) || this.$el.contains(event.target) || (this.overlay && this.overlay.contains(event.target)));
}, },
isNavIconClicked(event) { isNavIconClicked(event) {
return ( return (this.previousButton && (this.previousButton.isSameNode(event.target) || this.previousButton.contains(event.target))) || (this.nextButton && (this.nextButton.isSameNode(event.target) || this.nextButton.contains(event.target)));
DomHandler.hasClass(event.target, 'p-datepicker-prev') ||
DomHandler.hasClass(event.target, 'p-datepicker-prev-icon') ||
DomHandler.hasClass(event.target, 'p-datepicker-next') ||
DomHandler.hasClass(event.target, 'p-datepicker-next-icon')
);
}, },
alignOverlay() { alignOverlay() {
if (this.touchUI) { if (this.touchUI) {
@ -1152,7 +945,7 @@ export default {
return; return;
} }
DomHandler.find(this.overlay, '.p-datepicker-calendar td span:not(.p-disabled)').forEach((cell) => (cell.tabIndex = -1)); DomHandler.find(this.overlay, 'table td span:not([data-p-disabled="true"])').forEach((cell) => (cell.tabIndex = -1));
if (event) { if (event) {
event.currentTarget.focus(); event.currentTarget.focus();
@ -1722,7 +1515,8 @@ export default {
if (!this.mask) { if (!this.mask) {
this.mask = document.createElement('div'); this.mask = document.createElement('div');
this.mask.style.zIndex = String(parseInt(this.overlay.style.zIndex, 10) - 1); this.mask.style.zIndex = String(parseInt(this.overlay.style.zIndex, 10) - 1);
DomHandler.addMultipleClasses(this.mask, 'p-datepicker-mask p-datepicker-mask-scrollblocker p-component-overlay p-component-overlay-enter'); this.mask.setAttribute('data-pc-section', 'datepicker-mask');
!this.isUnstyled && DomHandler.addMultipleClasses(this.mask, 'p-datepicker-mask p-datepicker-mask-scrollblocker p-component-overlay p-component-overlay-enter');
this.maskClickListener = () => { this.maskClickListener = () => {
this.overlayVisible = false; this.overlayVisible = false;
@ -1731,15 +1525,20 @@ export default {
this.mask.addEventListener('click', this.maskClickListener); this.mask.addEventListener('click', this.maskClickListener);
document.body.appendChild(this.mask); document.body.appendChild(this.mask);
DomHandler.addClass(document.body, 'p-overflow-hidden'); document.body.setAttribute('data-p-overflow-hidden', 'true');
!this.isUnstyled && DomHandler.addClass(document.body, 'p-overflow-hidden');
} }
}, },
disableModality() { disableModality() {
if (this.mask) { if (this.mask) {
DomHandler.addClass(this.mask, 'p-component-overlay-leave'); if (this.isUnstyled) {
this.mask.addEventListener('animationend', () => {
this.destroyMask(); this.destroyMask();
}); } else {
DomHandler.addClass(this.mask, 'p-component-overlay-leave');
this.mask.addEventListener('animationend', () => {
this.destroyMask();
});
}
} }
}, },
destroyMask() { destroyMask() {
@ -1754,14 +1553,15 @@ export default {
for (let i = 0; i < bodyChildren.length; i++) { for (let i = 0; i < bodyChildren.length; i++) {
let bodyChild = bodyChildren[i]; let bodyChild = bodyChildren[i];
if (DomHandler.hasClass(bodyChild, 'p-datepicker-mask-scrollblocker')) { if (DomHandler.isAttributeEquals(bodyChild, 'data-pc-section', 'datepicker-mask')) {
hasBlockerMasks = true; hasBlockerMasks = true;
break; break;
} }
} }
if (!hasBlockerMasks) { if (!hasBlockerMasks) {
DomHandler.removeClass(document.body, 'p-overflow-hidden'); document.body.removeAttribute('data-p-overflow-hidden');
!this.isUnstyled && DomHandler.removeClass(document.body, 'p-overflow-hidden');
} }
}, },
updateCurrentMetaData() { updateCurrentMetaData() {
@ -2087,7 +1887,7 @@ export default {
let hasNextFocusableDate = nextTableRows.find((el) => { let hasNextFocusableDate = nextTableRows.find((el) => {
let focusCell = el.children[cellIndex].children[0]; let focusCell = el.children[cellIndex].children[0];
return !DomHandler.hasClass(focusCell, 'p-disabled'); return !DomHandler.getAttribute(focusCell, 'data-p-disabled');
}); });
if (hasNextFocusableDate) { if (hasNextFocusableDate) {
@ -2120,7 +1920,7 @@ export default {
let hasNextFocusableDate = prevTableRows.find((el) => { let hasNextFocusableDate = prevTableRows.find((el) => {
let focusCell = el.children[cellIndex].children[0]; let focusCell = el.children[cellIndex].children[0];
return !DomHandler.hasClass(focusCell, 'p-disabled'); return !DomHandler.getAttribute(focusCell, 'data-p-disabled');
}); });
if (hasNextFocusableDate) { if (hasNextFocusableDate) {
@ -2152,7 +1952,7 @@ export default {
let hasNextFocusableDate = prevCells.find((el) => { let hasNextFocusableDate = prevCells.find((el) => {
let focusCell = el.children[0]; let focusCell = el.children[0];
return !DomHandler.hasClass(focusCell, 'p-disabled'); return !DomHandler.getAttribute(focusCell, 'data-p-disabled');
}); });
if (hasNextFocusableDate) { if (hasNextFocusableDate) {
@ -2181,7 +1981,7 @@ export default {
let hasNextFocusableDate = nextCells.find((el) => { let hasNextFocusableDate = nextCells.find((el) => {
let focusCell = el.children[0]; let focusCell = el.children[0];
return !DomHandler.hasClass(focusCell, 'p-disabled'); return !DomHandler.getAttribute(focusCell, 'data-p-disabled');
}); });
if (hasNextFocusableDate) { if (hasNextFocusableDate) {
@ -2227,7 +2027,7 @@ export default {
let currentRow = cell.parentElement; let currentRow = cell.parentElement;
let focusCell = currentRow.children[0].children[0]; let focusCell = currentRow.children[0].children[0];
if (DomHandler.hasClass(focusCell, 'p-disabled')) { if (DomHandler.getAttribute(focusCell, 'data-p-disabled')) {
this.navigateToMonth(event, true, groupIndex); this.navigateToMonth(event, true, groupIndex);
} else { } else {
focusCell.tabIndex = '0'; focusCell.tabIndex = '0';
@ -2243,7 +2043,7 @@ export default {
let currentRow = cell.parentElement; let currentRow = cell.parentElement;
let focusCell = currentRow.children[currentRow.children.length - 1].children[0]; let focusCell = currentRow.children[currentRow.children.length - 1].children[0];
if (DomHandler.hasClass(focusCell, 'p-disabled')) { if (DomHandler.getAttribute(focusCell, 'data-p-disabled')) {
this.navigateToMonth(event, false, groupIndex); this.navigateToMonth(event, false, groupIndex);
} else { } else {
focusCell.tabIndex = '0'; focusCell.tabIndex = '0';
@ -2288,7 +2088,7 @@ export default {
this.navBackward(event); this.navBackward(event);
} else { } else {
let prevMonthContainer = this.overlay.children[groupIndex - 1]; let prevMonthContainer = this.overlay.children[groupIndex - 1];
let cells = DomHandler.find(prevMonthContainer, '.p-datepicker-calendar td span:not(.p-disabled):not(.p-ink)'); let cells = DomHandler.find(prevMonthContainer, 'table td span:not([data-p-disabled="true"]):not([data-p-ink="true"])');
let focusCell = cells[cells.length - 1]; let focusCell = cells[cells.length - 1];
focusCell.tabIndex = '0'; focusCell.tabIndex = '0';
@ -2300,7 +2100,7 @@ export default {
this.navForward(event); this.navForward(event);
} else { } else {
let nextMonthContainer = this.overlay.children[groupIndex + 1]; let nextMonthContainer = this.overlay.children[groupIndex + 1];
let focusCell = DomHandler.findSingle(nextMonthContainer, '.p-datepicker-calendar td span:not(.p-disabled):not(.p-ink)'); let focusCell = DomHandler.findSingle(nextMonthContainer, 'table td span:not([data-p-disabled="true"]):not([data-p-ink="true"])');
focusCell.tabIndex = '0'; focusCell.tabIndex = '0';
focusCell.focus(); focusCell.focus();
@ -2500,18 +2300,18 @@ export default {
if (this.navigationState.button) { if (this.navigationState.button) {
this.initFocusableCell(); this.initFocusableCell();
if (this.navigationState.backward) DomHandler.findSingle(this.overlay, '.p-datepicker-prev').focus(); if (this.navigationState.backward) this.previousButton.focus();
else DomHandler.findSingle(this.overlay, '.p-datepicker-next').focus(); else this.nextButton.focus();
} else { } else {
if (this.navigationState.backward) { if (this.navigationState.backward) {
let cells; let cells;
if (this.currentView === 'month') { if (this.currentView === 'month') {
cells = DomHandler.find(this.overlay, '.p-monthpicker .p-monthpicker-month:not(.p-disabled)'); cells = DomHandler.find(this.overlay, '[data-pc-section="monthpicker"] [data-pc-section="month"]:not([data-p-disabled="true"])');
} else if (this.currentView === 'year') { } else if (this.currentView === 'year') {
cells = DomHandler.find(this.overlay, '.p-yearpicker .p-yearpicker-year:not(.p-disabled)'); cells = DomHandler.find(this.overlay, '[data-pc-section="yearpicker"] [data-pc-section="year"]:not([data-p-disabled="true"])');
} else { } else {
cells = DomHandler.find(this.overlay, '.p-datepicker-calendar td span:not(.p-disabled):not(.p-ink)'); cells = DomHandler.find(this.overlay, 'table td span:not([data-p-disabled="true"]):not([data-p-ink="true"])');
} }
if (cells && cells.length > 0) { if (cells && cells.length > 0) {
@ -2519,11 +2319,11 @@ export default {
} }
} else { } else {
if (this.currentView === 'month') { if (this.currentView === 'month') {
cell = DomHandler.findSingle(this.overlay, '.p-monthpicker .p-monthpicker-month:not(.p-disabled)'); cell = DomHandler.findSingle(this.overlay, '[data-pc-section="monthpicker"] [data-pc-section="month"]:not([data-p-disabled="true"])');
} else if (this.currentView === 'year') { } else if (this.currentView === 'year') {
cell = DomHandler.findSingle(this.overlay, '.p-yearpicker .p-yearpicker-year:not(.p-disabled)'); cell = DomHandler.findSingle(this.overlay, '[data-pc-section="yearpicker"] [data-pc-section="year"]:not([data-p-disabled="true"])');
} else { } else {
cell = DomHandler.findSingle(this.overlay, '.p-datepicker-calendar td span:not(.p-disabled):not(.p-ink)'); cell = DomHandler.findSingle(this.overlay, 'table td span:not([data-p-disabled="true"]):not([data-p-ink="true"])');
} }
} }
@ -2542,25 +2342,25 @@ export default {
let cell; let cell;
if (this.currentView === 'month') { if (this.currentView === 'month') {
let cells = DomHandler.find(this.overlay, '.p-monthpicker .p-monthpicker-month'); let cells = DomHandler.find(this.overlay, '[data-pc-section="monthpicker"] [data-pc-section="month"]');
let selectedCell = DomHandler.findSingle(this.overlay, '.p-monthpicker .p-monthpicker-month.p-highlight'); let selectedCell = DomHandler.findSingle(this.overlay, '[data-pc-section="monthpicker"] [data-pc-section="month"][data-p-highlight="true"]');
cells.forEach((cell) => (cell.tabIndex = -1)); cells.forEach((cell) => (cell.tabIndex = -1));
cell = selectedCell || cells[0]; cell = selectedCell || cells[0];
} else if (this.currentView === 'year') { } else if (this.currentView === 'year') {
let cells = DomHandler.find(this.overlay, '.p-yearpicker .p-yearpicker-year'); let cells = DomHandler.find(this.overlay, '[data-pc-section="yearpicker"] [data-pc-section="year"]');
let selectedCell = DomHandler.findSingle(this.overlay, '.p-yearpicker .p-yearpicker-year.p-highlight'); let selectedCell = DomHandler.findSingle(this.overlay, '[data-pc-section="yearpicker"] [data-pc-section="year"][data-p-highlight="true"]');
cells.forEach((cell) => (cell.tabIndex = -1)); cells.forEach((cell) => (cell.tabIndex = -1));
cell = selectedCell || cells[0]; cell = selectedCell || cells[0];
} else { } else {
cell = DomHandler.findSingle(this.overlay, 'span.p-highlight'); cell = DomHandler.findSingle(this.overlay, 'span[data-p-highlight="true"]');
if (!cell) { if (!cell) {
let todayCell = DomHandler.findSingle(this.overlay, 'td.p-datepicker-today span:not(.p-disabled):not(.p-ink'); let todayCell = DomHandler.findSingle(this.overlay, 'td[data-p-today="true"] span:not([data-p-disabled="true"]):not([data-p-ink="true"]');
if (todayCell) cell = todayCell; if (todayCell) cell = todayCell;
else cell = DomHandler.findSingle(this.overlay, '.p-datepicker-calendar td span:not(.p-disabled):not(.p-ink'); else cell = DomHandler.findSingle(this.overlay, 'table td span:not([data-p-disabled="true"]):not([data-p-ink="true"]');
} }
} }
@ -2686,6 +2486,12 @@ export default {
inputRef(el) { inputRef(el) {
this.input = el; this.input = el;
}, },
previousButtonRef(el) {
this.previousButton = el;
},
nextButtonRef(el) {
this.nextButton = el;
},
getMonthName(index) { getMonthName(index) {
return this.$primevue.config.locale.monthNames[index]; return this.$primevue.config.locale.monthNames[index];
}, },
@ -2715,7 +2521,7 @@ export default {
this.onOverlayClick(event); this.onOverlayClick(event);
}, },
createResponsiveStyle() { createResponsiveStyle() {
if (this.numberOfMonths > 1 && this.responsiveOptions) { if (this.numberOfMonths > 1 && this.responsiveOptions && !this.isUnstyled) {
if (!this.responsiveStyleElement) { if (!this.responsiveStyleElement) {
this.responsiveStyleElement = document.createElement('style'); this.responsiveStyleElement = document.createElement('style');
this.responsiveStyleElement.type = 'text/css'; this.responsiveStyleElement.type = 'text/css';
@ -2792,35 +2598,6 @@ export default {
inputFieldValue() { inputFieldValue() {
return this.formatValue(this.modelValue); return this.formatValue(this.modelValue);
}, },
containerClass() {
return [
'p-calendar p-component p-inputwrapper',
{
'p-calendar-w-btn': this.showIcon,
'p-calendar-timeonly': this.timeOnly,
'p-calendar-disabled': this.disabled,
'p-inputwrapper-filled': this.modelValue,
'p-inputwrapper-focus': this.focused
}
];
},
panelStyleClass() {
return [
'p-datepicker p-component',
this.panelClass,
{
'p-datepicker-inline': this.inline,
'p-disabled': this.disabled,
'p-datepicker-timeonly': this.timeOnly,
'p-datepicker-multiple-month': this.numberOfMonths > 1,
'p-datepicker-monthpicker': this.currentView === 'month',
'p-datepicker-yearpicker': this.currentView === 'year',
'p-datepicker-touch-ui': this.touchUI,
'p-input-filled': this.$primevue.config.inputStyle === 'filled',
'p-ripple-disabled': this.$primevue.config.ripple === false
}
];
},
months() { months() {
let months = []; let months = [];
@ -3037,160 +2814,3 @@ export default {
} }
}; };
</script> </script>
<style>
.p-calendar {
position: relative;
display: inline-flex;
max-width: 100%;
}
.p-calendar .p-inputtext {
flex: 1 1 auto;
width: 1%;
}
.p-calendar-w-btn .p-inputtext {
border-top-right-radius: 0;
border-bottom-right-radius: 0;
}
.p-calendar-w-btn .p-datepicker-trigger {
border-top-left-radius: 0;
border-bottom-left-radius: 0;
}
/* Fluid */
.p-fluid .p-calendar {
display: flex;
}
.p-fluid .p-calendar .p-inputtext {
width: 1%;
}
/* Datepicker */
.p-calendar .p-datepicker {
min-width: 100%;
}
.p-datepicker {
width: auto;
position: absolute;
top: 0;
left: 0;
}
.p-datepicker-inline {
display: inline-block;
position: static;
overflow-x: auto;
}
/* Header */
.p-datepicker-header {
display: flex;
align-items: center;
justify-content: space-between;
}
.p-datepicker-header .p-datepicker-title {
margin: 0 auto;
}
.p-datepicker-prev,
.p-datepicker-next {
cursor: pointer;
display: inline-flex;
justify-content: center;
align-items: center;
overflow: hidden;
position: relative;
}
/* Multiple Month DatePicker */
.p-datepicker-multiple-month .p-datepicker-group-container {
display: flex;
}
.p-datepicker-multiple-month .p-datepicker-group-container .p-datepicker-group {
flex: 1 1 auto;
}
/* DatePicker Table */
.p-datepicker table {
width: 100%;
border-collapse: collapse;
}
.p-datepicker td > span {
display: flex;
justify-content: center;
align-items: center;
cursor: pointer;
margin: 0 auto;
overflow: hidden;
position: relative;
}
/* Month Picker */
.p-monthpicker-month {
width: 33.3%;
display: inline-flex;
align-items: center;
justify-content: center;
cursor: pointer;
overflow: hidden;
position: relative;
}
/* Year Picker */
.p-yearpicker-year {
width: 50%;
display: inline-flex;
align-items: center;
justify-content: center;
cursor: pointer;
overflow: hidden;
position: relative;
}
/* Button Bar */
.p-datepicker-buttonbar {
display: flex;
justify-content: space-between;
align-items: center;
}
/* Time Picker */
.p-timepicker {
display: flex;
justify-content: center;
align-items: center;
}
.p-timepicker button {
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
overflow: hidden;
position: relative;
}
.p-timepicker > div {
display: flex;
align-items: center;
flex-direction: column;
}
/* Touch UI */
.p-datepicker-touch-ui,
.p-calendar .p-datepicker-touch-ui {
position: fixed;
top: 50%;
left: 50%;
min-width: 80vw;
transform: translate(-50%, -50%);
}
</style>

View File

@ -16,6 +16,7 @@ function create(el) {
ink.className = 'p-ink'; ink.className = 'p-ink';
ink.setAttribute('role', 'presentation'); ink.setAttribute('role', 'presentation');
ink.setAttribute('aria-hidden', 'true'); ink.setAttribute('aria-hidden', 'true');
ink.setAttribute('data-p-ink', 'true');
el.appendChild(ink); el.appendChild(ink);
ink.addEventListener('animationend', onAnimationEnd); ink.addEventListener('animationend', onAnimationEnd);

View File

@ -167,6 +167,12 @@ export default {
return false; return false;
}, },
addStyles(element, styles = {}) {
if (element) {
Object.entries(styles).forEach(([key, value]) => (element.style[key] = value));
}
},
find(element, selector) { find(element, selector) {
return this.isElement(element) ? element.querySelectorAll(selector) : []; return this.isElement(element) ? element.querySelectorAll(selector) : [];
}, },
@ -175,6 +181,32 @@ export default {
return this.isElement(element) ? element.querySelector(selector) : null; return this.isElement(element) ? element.querySelector(selector) : null;
}, },
getAttribute(element, name) {
if (element) {
const value = element.getAttribute(name);
if (!isNaN(value)) {
return +value;
}
if (value === 'true' || value === 'false') {
return value === 'true';
}
return value;
}
return undefined;
},
isAttributeEquals(element, name, value) {
return element ? this.getAttribute(element, name) === value : false;
},
isAttributeNotEquals(element, name, value) {
return !this.isAttributeEquals(element, name, value);
},
getHeight(el) { getHeight(el) {
if (el) { if (el) {
let height = el.offsetHeight; let height = el.offsetHeight;
@ -203,15 +235,15 @@ export default {
absolutePosition(element, target) { absolutePosition(element, target) {
if (element) { if (element) {
let elementDimensions = element.offsetParent ? { width: element.offsetWidth, height: element.offsetHeight } : this.getHiddenElementDimensions(element); const elementDimensions = element.offsetParent ? { width: element.offsetWidth, height: element.offsetHeight } : this.getHiddenElementDimensions(element);
let elementOuterHeight = elementDimensions.height; const elementOuterHeight = elementDimensions.height;
let elementOuterWidth = elementDimensions.width; const elementOuterWidth = elementDimensions.width;
let targetOuterHeight = target.offsetHeight; const targetOuterHeight = target.offsetHeight;
let targetOuterWidth = target.offsetWidth; const targetOuterWidth = target.offsetWidth;
let targetOffset = target.getBoundingClientRect(); const targetOffset = target.getBoundingClientRect();
let windowScrollTop = this.getWindowScrollTop(); const windowScrollTop = this.getWindowScrollTop();
let windowScrollLeft = this.getWindowScrollLeft(); const windowScrollLeft = this.getWindowScrollLeft();
let viewport = this.getViewport(); const viewport = this.getViewport();
let top, left; let top, left;
if (targetOffset.top + targetOuterHeight + elementOuterHeight > viewport.height) { if (targetOffset.top + targetOuterHeight + elementOuterHeight > viewport.height) {
@ -236,7 +268,7 @@ export default {
relativePosition(element, target) { relativePosition(element, target) {
if (element) { if (element) {
let elementDimensions = element.offsetParent ? { width: element.offsetWidth, height: element.offsetHeight } : this.getHiddenElementDimensions(element); const elementDimensions = element.offsetParent ? { width: element.offsetWidth, height: element.offsetHeight } : this.getHiddenElementDimensions(element);
const targetHeight = target.offsetHeight; const targetHeight = target.offsetHeight;
const targetOffset = target.getBoundingClientRect(); const targetOffset = target.getBoundingClientRect();
const viewport = this.getViewport(); const viewport = this.getViewport();