feat(datetime): add ability to select only month, year, or month and year (#23913)

This commit is contained in:
Liam DeBeasi
2021-09-14 09:10:43 -04:00
committed by GitHub
parent c50d895370
commit 4ae44b7a23
10 changed files with 323 additions and 338 deletions

View File

@ -384,7 +384,7 @@ ion-datetime,prop,minuteValues,number | number[] | string | undefined,undefined,
ion-datetime,prop,mode,"ios" | "md",undefined,false,false
ion-datetime,prop,monthValues,number | number[] | string | undefined,undefined,false,false
ion-datetime,prop,name,string,this.inputId,false,false
ion-datetime,prop,presentation,"date" | "date-time" | "time" | "time-date",'date-time',false,false
ion-datetime,prop,presentation,"date" | "date-time" | "month" | "month-year" | "time" | "time-date" | "year",'date-time',false,false
ion-datetime,prop,readonly,boolean,false,false,false
ion-datetime,prop,showDefaultButtons,boolean,false,false,false
ion-datetime,prop,showDefaultTimeLabel,boolean,true,false,false

View File

@ -771,7 +771,7 @@ export namespace Components {
/**
* Which values you want to select. `'date'` will show a calendar picker to select the month, day, and year. `'time'` will show a time picker to select the hour, minute, and (optionally) AM/PM. `'date-time'` will show the date picker first and time picker second. `'time-date'` will show the time picker first and date picker second.
*/
"presentation": 'date-time' | 'time-date' | 'date' | 'time';
"presentation": 'date-time' | 'time-date' | 'date' | 'time' | 'month' | 'year' | 'month-year';
/**
* If `true`, the datetime appears normal but is not interactive.
*/
@ -4379,7 +4379,7 @@ declare namespace LocalJSX {
/**
* Which values you want to select. `'date'` will show a calendar picker to select the month, day, and year. `'time'` will show a time picker to select the hour, minute, and (optionally) AM/PM. `'date-time'` will show the date picker first and time picker second. `'time-date'` will show the time picker first and date picker second.
*/
"presentation"?: 'date-time' | 'time-date' | 'date' | 'time';
"presentation"?: 'date-time' | 'time-date' | 'date' | 'time' | 'month' | 'year' | 'month-year';
/**
* If `true`, the datetime appears normal but is not interactive.
*/

View File

@ -8,7 +8,9 @@
--title-color: #{$text-color-step-400};
}
:host(:not(.datetime-presentation-time)) {
:host(.datetime-presentation-date-time),
:host(.datetime-presentation-time-date),
:host(.datetime-presentation-date) {
min-height: 300px;
}
@ -156,58 +158,29 @@
line-height: $datetime-ios-time-height;
}
// Year Picker
// Month and Year Picker
// -----------------------------------
:host(.show-month-and-year) .calendar-action-buttons ion-item {
--color: #{current-color(base)};
}
:host .datetime-year-body .datetime-picker-col {
@include margin(0, 10px, 0, 10px);
@include padding(0, $datetime-ios-padding, 0, $datetime-ios-padding);
}
:host .datetime-picker-before {
@include position(0, null, null, 0);
position: absolute;
width: 100%;
height: 82px;
background: linear-gradient(to bottom, var(--background, var(--ion-background-color, #fff)) 20%, rgba(var(--background-rgb, var(--ion-background-color-rgb, 255, 255, 255)), 0.8) 100%);
z-index: 10;
pointer-events: none;
}
:host .datetime-picker-after {
@include position(116px, null, null, 0);
position: absolute;
width: 100%;
height: 115px;
background: linear-gradient(to top, var(--background, var(--ion-background-color, #fff)) 30%, rgba(var(--background-rgb, var(--ion-background-color-rgb, 255, 255, 255)), 0.8) 100%);
z-index: 10;
pointer-events: none;
}
:host .datetime-picker-highlight {
@include position(50%, 0, 0, 0);
@include border-radius($datetime-ios-time-border-radius, $datetime-ios-time-border-radius, $datetime-ios-time-border-radius, $datetime-ios-time-border-radius);
@include position(50%, 0, 0, 0);
@include margin(0, auto, 0, auto);
position: absolute;
width: calc(100% - #{$datetime-ios-padding * 2});
height: 34px;
transform: translateY(-50%);
@ -217,78 +190,6 @@
z-index: -1;
}
:host .datetime-year-body {
display: flex;
position: relative;
align-items: center;
justify-content: center;
font-size: 22px;
/**
* This is required otherwise the
* highlight will appear behind
* the datetime.
*/
z-index: 0;
}
:host .datetime-picker-col {
scroll-snap-type: y mandatory;
/**
* Need to explicitly set overflow-x: hidden
* for older implementations of scroll snapping.
*/
overflow-x: hidden;
overflow-y: scroll;
// Hide scrollbars on Firefox
scrollbar-width: none;
height: 200px;
outline: none;
}
@media (any-hover: hover) {
:host .datetime-picker-col:focus {
background: current-color(base, 0.2);
}
}
/**
* Hide scrollbars on Chrome and Safari
*/
:host .datetime-picker-col::-webkit-scrollbar {
display: none;
}
:host .picker-col-item {
height: 34px;
line-height: 34px;
scroll-snap-align: center;
}
:host .picker-col-item-empty {
scroll-snap-align: none;
}
:host .datetime-year-body .datetime-picker-col:first-of-type {
text-align: left;
}
:host .datetime-year-body .datetime-picker-col:last-of-type {
text-align: right;
}
// Footer
// -----------------------------------
:host .datetime-buttons {

View File

@ -150,96 +150,33 @@
background: var(--background-checked);
}
// Year Picker
// Month and Year
// -----------------------------------
:host(.show-month-and-year) .datetime-calendar {
flex: 0;
:host .datetime-picker-col {
@include border-radius($datetime-md-wheel-border-radius, $datetime-md-wheel-border-radius, $datetime-md-wheel-border-radius, $datetime-md-wheel-border-radius);
@include padding(null, $datetime-md-wheel-padding, null, $datetime-md-wheel-padding);
}
:host(.show-month-and-year) .datetime-year {
flex: 1;
min-height: 0;
:host .picker-col-item {
font-size: 18px;
}
:host(.show-month-and-year) .datetime-footer {
border-top: 1px solid var(--ion-color-step-250, #dddddd);
:host .datetime-picker-before {
background: linear-gradient(to bottom, var(--background, var(--ion-background-color, #fff)) 20%, rgba(var(--background-rgb, var(--ion-background-color-rgb, 255, 255, 255)), 0) 90%);
}
:host .datetime-year-body {
@include padding(0, $datetime-md-padding, $datetime-md-padding, $datetime-md-padding);
display: grid;
grid-template-columns: repeat(auto-fit, minmax(74px, 1fr));
grid-gap: 0px 6px;
overflow-y: scroll;
-webkit-overflow-scrolling: touch;
}
:host .datetime-year-item {
@include padding(0px, 0px, 0px, 0px);
@include margin(0px, 0px, 0px, 0px);
position: relative;
align-items: center;
justify-content: center;
border: none;
outline: none;
background: none;
color: currentColor;
cursor: pointer;
appearance: none;
z-index: 0;
}
:host .datetime-year-item[disabled] {
pointer-events: none;
opacity: 0.4;
}
:host .datetime-year-item .datetime-year-inner {
@include border-radius(20px, 20px, 20px, 20px);
@include margin(10px, 10px, 10px, 10px);
display: flex;
align-items: center;
justify-content: center;
height: 32px;
border: 1px solid transparent;
font-size: 16px;
:host .datetime-picker-after {
background: linear-gradient(to top, var(--background, var(--ion-background-color, #fff)) 30%, rgba(var(--background-rgb, var(--ion-background-color-rgb, 255, 255, 255)), 0) 90%);
}
:host .datetime-current-year .datetime-year-inner {
border: 1px solid current-color(base);
color: current-color(base);
}
:host .datetime-active-year .datetime-year-inner {
border: 1px solid current-color(base);
background: current-color(base);
color: current-color(contrast);
}
@media (any-hover: hover) {
:host .datetime-year-item:hover,
:host .datetime-year-item:focus {
background: current-color(base, 0.1);
}
/**
* Add some margin when only selecting month/year
* otherwise layout will too constricted.
*/
:host(.datetime-presentation-month) .datetime-year,
:host(.datetime-presentation-year) .datetime-year,
:host(.datetime-presentation-month-year) .datetime-year {
@include margin(20px, null, 20px, null);
}
// Footer
@ -257,3 +194,7 @@
:host .datetime-view-buttons ion-button {
color: $text-color-step-200;
}
:host .picker-col-item-active {
color: current-color(base);
}

View File

@ -24,3 +24,9 @@ $datetime-md-time-width: 68px !default;
/// @prop - Border radius of the time picker
$datetime-md-time-border-radius: 4px !default;
/// @prop - Border radius of the month and year wheel
$datetime-md-wheel-border-radius: 8px !default;
/// @prop - Padding of the month and year wheel
$datetime-md-wheel-padding: 8px !default;

View File

@ -19,7 +19,9 @@
overflow: hidden;
}
:host(:not(.datetime-presentation-time)) {
:host(.datetime-presentation-date-time),
:host(.datetime-presentation-time-date),
:host(.datetime-presentation-date) {
height: 100%;
}
@ -387,3 +389,125 @@
:host(.in-item) {
position: static;
}
// Year Picker
// -----------------------------------
:host(.show-month-and-year) .calendar-action-buttons ion-item {
--color: #{current-color(base)};
}
:host .datetime-year-body .datetime-picker-col {
@include margin(0, 10px, 0, 10px);
}
:host .datetime-picker-before {
@include position(0, null, null, 0);
position: absolute;
width: 100%;
height: 82px;
background: linear-gradient(to bottom, var(--background, var(--ion-background-color, #fff)) 20%, rgba(var(--background-rgb, var(--ion-background-color-rgb, 255, 255, 255)), 0.7) 100%);
z-index: 10;
pointer-events: none;
}
:host .datetime-picker-after {
@include position(116px, null, null, 0);
position: absolute;
width: 100%;
height: 115px;
background: linear-gradient(to top, var(--background, var(--ion-background-color, #fff)) 30%, rgba(var(--background-rgb, var(--ion-background-color-rgb, 255, 255, 255)), 0.7) 100%);
z-index: 10;
pointer-events: none;
}
:host .datetime-year-body {
display: flex;
position: relative;
align-items: center;
justify-content: center;
font-size: 22px;
/**
* This is required otherwise the
* highlight will appear behind
* the datetime.
*/
z-index: 0;
}
:host .datetime-picker-col {
scroll-snap-type: y mandatory;
/**
* Need to explicitly set overflow-x: hidden
* for older implementations of scroll snapping.
*/
overflow-x: hidden;
overflow-y: scroll;
// Hide scrollbars on Firefox
scrollbar-width: none;
height: 200px;
outline: none;
}
@media (any-hover: hover) {
:host .datetime-picker-col:focus {
background: current-color(base, 0.2);
}
}
/**
* Hide scrollbars on Chrome and Safari
*/
:host .datetime-picker-col::-webkit-scrollbar {
display: none;
}
:host .picker-col-item {
height: 38px;
line-height: 38px;
scroll-snap-align: center;
}
:host .picker-col-item-empty {
scroll-snap-align: none;
}
:host .datetime-year-body .datetime-picker-col:first-of-type {
text-align: left;
}
:host .datetime-year-body .datetime-picker-col:last-of-type {
text-align: right;
}
/**
* Adding :last-of-type is needed here so that
* we can achieve higher specificity than the
* previous selectors and avoid using !important.
*/
:host(.datetime-presentation-year) .datetime-picker-col:last-of-type,
:host(.datetime-presentation-month) .datetime-picker-col:last-of-type {
text-align: center;
}

View File

@ -53,7 +53,6 @@ import {
} from './utils/parse';
import {
getCalendarDayState,
getCalendarYearState,
isDayDisabled
} from './utils/state';
@ -188,7 +187,7 @@ export class Datetime implements ComponentInterface {
* AM/PM. `'date-time'` will show the date picker first and time picker second.
* `'time-date'` will show the time picker first and date picker second.
*/
@Prop() presentation: 'date-time' | 'time-date' | 'date' | 'time' = 'date-time';
@Prop() presentation: 'date-time' | 'time-date' | 'date' | 'time' | 'month' | 'year' | 'month-year' = 'date-time';
/**
* The text to display on the picker's cancel button.
@ -814,8 +813,6 @@ export class Datetime implements ComponentInterface {
}
componentDidLoad() {
const mode = getIonMode(this);
/**
* If a scrollable element is hidden using `display: none`,
* it will not have a scroll height meaning we cannot scroll elements
@ -832,10 +829,7 @@ export class Datetime implements ComponentInterface {
this.initializeKeyboardListeners();
this.initializeTimeScrollListener();
this.initializeOverlayListener();
if (mode === 'ios') {
this.initializeMonthAndYearScrollListeners();
}
this.initializeMonthAndYearScrollListeners();
/**
* TODO: Datetime needs a frame to ensure that it
@ -891,24 +885,24 @@ export class Datetime implements ComponentInterface {
}
private initializeMonthAndYearScrollListeners = () => {
const { monthRef, yearRef } = this;
if (!yearRef || !monthRef) { return; }
const { year, month } = this.workingParts;
const { monthRef, yearRef, workingParts } = this;
const { year, month } = workingParts;
/**
* Scroll initial month and year into view.
* scrollIntoView() will scroll entire page
* if element is not in viewport. Use scrollTop instead.
*/
const initialYear = yearRef.querySelector(`.picker-col-item[data-value="${year}"]`) as HTMLElement | null;
if (initialYear) {
yearRef.scrollTop = initialYear.offsetTop - (initialYear.clientHeight * 2);
let activeYearEl = yearRef?.querySelector(`.picker-col-item[data-value="${year}"]`) as HTMLElement | null;
if (activeYearEl) {
yearRef!.scrollTop = activeYearEl.offsetTop - (activeYearEl.clientHeight * 2);
activeYearEl.classList.add(PICKER_COL_ACTIVE);
}
const initialMonth = monthRef.querySelector(`.picker-col-item[data-value="${month}"]`) as HTMLElement | null;
if (initialMonth) {
monthRef.scrollTop = initialMonth.offsetTop - (initialMonth.clientHeight * 2);
let activeMonthEl = monthRef?.querySelector(`.picker-col-item[data-value="${month}"]`) as HTMLElement | null;
if (activeMonthEl) {
monthRef!.scrollTop = activeMonthEl.offsetTop - (activeMonthEl.clientHeight * 2);
activeMonthEl.classList.add(PICKER_COL_ACTIVE)
}
let timeout: any;
@ -920,18 +914,31 @@ export class Datetime implements ComponentInterface {
}
const activeCol = colType === 'month' ? monthRef : yearRef;
timeout = setTimeout(() => {
if (!activeCol) { return; }
const bbox = activeCol.getBoundingClientRect();
const bbox = activeCol.getBoundingClientRect();
/**
* Select item in the center of the column
* which is the month/year that we want to select
*/
const centerX = bbox.x + (bbox.width / 2);
const centerY = bbox.y + (bbox.height / 2);
/**
* Select item in the center of the column
* which is the month/year that we want to select
*/
const centerX = bbox.x + (bbox.width / 2);
const centerY = bbox.y + (bbox.height / 2);
const activeElement = this.el!.shadowRoot!.elementFromPoint(centerX, centerY) as HTMLElement;
const prevActiveEl = colType === 'month' ? activeMonthEl : activeYearEl;
if (prevActiveEl !== null) {
prevActiveEl.classList.remove(PICKER_COL_ACTIVE);
}
const activeElement = this.el!.shadowRoot!.elementFromPoint(centerX, centerY)!;
if (colType === 'month') {
activeMonthEl = activeElement;
} else if (colType === 'year') {
activeYearEl = activeElement;
}
activeElement.classList.add(PICKER_COL_ACTIVE);
timeout = setTimeout(() => {
const dataValue = activeElement.getAttribute('data-value');
/**
@ -944,16 +951,35 @@ export class Datetime implements ComponentInterface {
}
const value = parseInt(dataValue, 10);
const { presentation } = this;
if (colType === 'month') {
this.setWorkingParts({
...this.workingParts,
month: value
});
/**
* If developers are only selecting month/month-year
* then we need to call ionChange as they will
* not be selecting dates too.
*/
if (presentation === 'month' || presentation === 'month-year') {
this.setActiveParts({
...this.activeParts,
month: value
});
}
} else {
this.setWorkingParts({
...this.workingParts,
year: value
});
if (presentation === 'year' || presentation === 'month-year') {
this.setActiveParts({
...this.activeParts,
year: value
});
}
}
/**
@ -963,14 +989,14 @@ export class Datetime implements ComponentInterface {
*/
raf(() => {
const { month: workingMonth, year: workingYear } = this.workingParts;
const monthEl = monthRef.querySelector(`.picker-col-item[data-value='${workingMonth}']`);
const yearEl = yearRef.querySelector(`.picker-col-item[data-value='${workingYear}']`);
const monthEl = monthRef?.querySelector(`.picker-col-item[data-value='${workingMonth}']`);
const yearEl = yearRef?.querySelector(`.picker-col-item[data-value='${workingYear}']`);
if (monthEl) {
if (monthEl && monthRef) {
this.centerPickerItemInView(monthEl as HTMLElement, monthRef, 'auto');
}
if (yearEl) {
if (yearEl && yearRef) {
this.centerPickerItemInView(yearEl as HTMLElement, yearRef, 'auto');
}
});
@ -985,12 +1011,12 @@ export class Datetime implements ComponentInterface {
raf(() => {
const monthScroll = () => scrollCallback('month');
const yearScroll = () => scrollCallback('year');
monthRef.addEventListener('scroll', monthScroll);
yearRef.addEventListener('scroll', yearScroll);
monthRef?.addEventListener('scroll', monthScroll);
yearRef?.addEventListener('scroll', yearScroll);
this.destroyMonthAndYearScroll = () => {
monthRef.removeEventListener('scroll', monthScroll);
yearRef.removeEventListener('scroll', yearScroll);
monthRef?.removeEventListener('scroll', monthScroll);
yearRef?.removeEventListener('scroll', yearScroll);
}
});
}
@ -1198,34 +1224,6 @@ export class Datetime implements ComponentInterface {
this.showMonthAndYear = !this.showMonthAndYear;
}
private renderMDYearView(calendarYears: number[] = []) {
return calendarYears.map(year => {
const { isCurrentYear, isActiveYear, ariaSelected } = getCalendarYearState(year, this.workingParts, this.todayParts);
return (
<button
aria-selected={ariaSelected}
class={{
'datetime-year-item': true,
'datetime-current-year': isCurrentYear,
'datetime-active-year': isActiveYear
}}
onClick={() => {
this.setWorkingParts({
...this.workingParts,
year
});
this.showMonthAndYear = false;
}}
>
<div class="datetime-year-inner">
{year}
</div>
</button>
)
})
}
private centerPickerItemInView(target: HTMLElement, container: HTMLElement, behavior: ScrollBehavior = 'smooth') {
container.scroll({
// (Vertical offset from parent) - (three empty picker rows) + (half the height of the target to ensure the scroll triggers)
@ -1235,54 +1233,52 @@ export class Datetime implements ComponentInterface {
});
}
private renderiOSYearView(calendarYears: number[] = []) {
return [
<div class="datetime-picker-before"></div>,
<div class="datetime-picker-after"></div>,
<div class="datetime-picker-highlight"></div>,
<div class="datetime-picker-col month-col" ref={el => this.monthRef = el} tabindex="0">
<div class="picker-col-item picker-col-item-empty">&nbsp;</div>
<div class="picker-col-item picker-col-item-empty">&nbsp;</div>
<div class="picker-col-item picker-col-item-empty">&nbsp;</div>
{getPickerMonths(this.locale, this.workingParts, this.minParts, this.maxParts, this.parsedMonthValues).map(month => {
return (
<div
class="picker-col-item"
data-value={month.value}
onClick={(ev: Event) => this.centerPickerItemInView(ev.target as HTMLElement, this.monthRef as HTMLElement)}
>{month.text}</div>
)
})}
<div class="picker-col-item picker-col-item-empty">&nbsp;</div>
<div class="picker-col-item picker-col-item-empty">&nbsp;</div>
<div class="picker-col-item picker-col-item-empty">&nbsp;</div>
</div>,
<div class="datetime-picker-col year-col" ref={el => this.yearRef = el} tabindex="0">
<div class="picker-col-item picker-col-item-empty">&nbsp;</div>
<div class="picker-col-item picker-col-item-empty">&nbsp;</div>
<div class="picker-col-item picker-col-item-empty">&nbsp;</div>
{calendarYears.map(year => {
return (
<div
class="picker-col-item"
data-value={year}
onClick={(ev: Event) => this.centerPickerItemInView(ev.target as HTMLElement, this.yearRef as HTMLElement)}
>{year}</div>
)
})}
<div class="picker-col-item picker-col-item-empty">&nbsp;</div>
<div class="picker-col-item picker-col-item-empty">&nbsp;</div>
<div class="picker-col-item picker-col-item-empty">&nbsp;</div>
</div>
]
}
private renderYearView(mode: Mode) {
private renderYearView() {
const { presentation } = this;
const calendarYears = getCalendarYears(this.todayParts, this.minParts, this.maxParts, this.parsedYearValues);
const showMonth = presentation !== 'year';
const showYear = presentation !== 'month';
return (
<div class="datetime-year">
<div class="datetime-year-body">
{mode === 'ios' ? this.renderiOSYearView(calendarYears) : this.renderMDYearView(calendarYears)}
<div class="datetime-picker-before"></div>
<div class="datetime-picker-after"></div>
<div class="datetime-picker-highlight"></div>
{showMonth && <div class="datetime-picker-col month-col" ref={el => this.monthRef = el} tabindex="0">
<div class="picker-col-item picker-col-item-empty">&nbsp;</div>
<div class="picker-col-item picker-col-item-empty">&nbsp;</div>
<div class="picker-col-item picker-col-item-empty">&nbsp;</div>
{getPickerMonths(this.locale, this.workingParts, this.minParts, this.maxParts, this.parsedMonthValues).map(month => {
return (
<div
class="picker-col-item"
data-value={month.value}
onClick={(ev: Event) => this.centerPickerItemInView(ev.target as HTMLElement, this.monthRef as HTMLElement)}
>{month.text}</div>
)
})}
<div class="picker-col-item picker-col-item-empty">&nbsp;</div>
<div class="picker-col-item picker-col-item-empty">&nbsp;</div>
<div class="picker-col-item picker-col-item-empty">&nbsp;</div>
</div>}
{showYear && <div class="datetime-picker-col year-col" ref={el => this.yearRef = el} tabindex="0">
<div class="picker-col-item picker-col-item-empty">&nbsp;</div>
<div class="picker-col-item picker-col-item-empty">&nbsp;</div>
<div class="picker-col-item picker-col-item-empty">&nbsp;</div>
{calendarYears.map(year => {
return (
<div
class="picker-col-item"
data-value={year}
onClick={(ev: Event) => this.centerPickerItemInView(ev.target as HTMLElement, this.yearRef as HTMLElement)}
>{year}</div>
)
})}
<div class="picker-col-item picker-col-item-empty">&nbsp;</div>
<div class="picker-col-item picker-col-item-empty">&nbsp;</div>
<div class="picker-col-item picker-col-item-empty">&nbsp;</div>
</div>}
</div>
</div>
);
@ -1519,7 +1515,7 @@ export class Datetime implements ComponentInterface {
return [
this.renderCalendarViewHeader(mode),
this.renderCalendar(mode),
this.renderYearView(mode),
this.renderYearView(),
this.renderTime(mode),
this.renderFooter()
]
@ -1528,7 +1524,7 @@ export class Datetime implements ComponentInterface {
this.renderCalendarViewHeader(mode),
this.renderTime(mode),
this.renderCalendar(mode),
this.renderYearView(mode),
this.renderYearView(),
this.renderFooter()
]
case 'time':
@ -1536,11 +1532,18 @@ export class Datetime implements ComponentInterface {
this.renderTime(mode),
this.renderFooter()
]
case 'date':
case 'month':
case 'month-year':
case 'year':
return [
this.renderYearView(),
this.renderFooter()
]
default:
return [
this.renderCalendarViewHeader(mode),
this.renderCalendar(mode),
this.renderYearView(mode),
this.renderYearView(),
this.renderFooter()
]
}
@ -1549,6 +1552,8 @@ export class Datetime implements ComponentInterface {
render() {
const { name, value, disabled, el, color, isPresented, readonly, showMonthAndYear, presentation, size } = this;
const mode = getIonMode(this);
const isMonthAndYearPresentation = presentation === 'year' || presentation === 'month' || presentation === 'month-year';
const shouldShowMonthAndYear = showMonthAndYear || isMonthAndYearPresentation;
renderHiddenInput(true, el, name, value, disabled);
@ -1561,7 +1566,7 @@ export class Datetime implements ComponentInterface {
['datetime-presented']: isPresented,
['datetime-readonly']: readonly,
['datetime-disabled']: disabled,
'show-month-and-year': showMonthAndYear,
'show-month-and-year': shouldShowMonthAndYear,
[`datetime-presentation-${presentation}`]: true,
[`datetime-size-${size}`]: true
})
@ -1574,3 +1579,4 @@ export class Datetime implements ComponentInterface {
}
let datetimeIds = 0;
const PICKER_COL_ACTIVE = 'picker-col-item-active';

View File

@ -637,31 +637,31 @@ export class DatetimeExample {
## Properties
| Property | Attribute | Description | Type | Default |
| ---------------------- | ------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------ | -------------- |
| `cancelText` | `cancel-text` | The text to display on the picker's cancel button. | `string` | `'Cancel'` |
| `color` | `color` | The color to use from your application's color palette. Default options are: `"primary"`, `"secondary"`, `"tertiary"`, `"success"`, `"warning"`, `"danger"`, `"light"`, `"medium"`, and `"dark"`. For more information on colors, see [theming](/docs/theming/basics). | `string \| undefined` | `'primary'` |
| `dayValues` | `day-values` | Values used to create the list of selectable days. By default every day is shown for the given month. However, to control exactly which days of the month to display, the `dayValues` input can take a number, an array of numbers, or a string of comma separated numbers. Note that even if the array days have an invalid number for the selected month, like `31` in February, it will correctly not show days which are not valid for the selected month. | `number \| number[] \| string \| undefined` | `undefined` |
| `disabled` | `disabled` | If `true`, the user cannot interact with the datetime. | `boolean` | `false` |
| `doneText` | `done-text` | The text to display on the picker's "Done" button. | `string` | `'Done'` |
| `firstDayOfWeek` | `first-day-of-week` | The first day of the week to use for `ion-datetime`. The default value is `0` and represents Sunday. | `number` | `0` |
| `hourCycle` | `hour-cycle` | The hour cycle of the `ion-datetime`. If no value is set, this is specified by the current locale. | `"h12" \| "h23" \| undefined` | `undefined` |
| `hourValues` | `hour-values` | Values used to create the list of selectable hours. By default the hour values range from `0` to `23` for 24-hour, or `1` to `12` for 12-hour. However, to control exactly which hours to display, the `hourValues` input can take a number, an array of numbers, or a string of comma separated numbers. | `number \| number[] \| string \| undefined` | `undefined` |
| `locale` | `locale` | The locale to use for `ion-datetime`. This impacts month and day name formatting. The `'default'` value refers to the default locale set by your device. | `string` | `'default'` |
| `max` | `max` | The maximum datetime allowed. Value must be a date string following the [ISO 8601 datetime format standard](https://www.w3.org/TR/NOTE-datetime), `1996-12-19`. The format does not have to be specific to an exact datetime. For example, the maximum could just be the year, such as `1994`. Defaults to the end of this year. | `string \| undefined` | `undefined` |
| `min` | `min` | The minimum datetime allowed. Value must be a date string following the [ISO 8601 datetime format standard](https://www.w3.org/TR/NOTE-datetime), such as `1996-12-19`. The format does not have to be specific to an exact datetime. For example, the minimum could just be the year, such as `1994`. Defaults to the beginning of the year, 100 years ago from today. | `string \| undefined` | `undefined` |
| `minuteValues` | `minute-values` | Values used to create the list of selectable minutes. By default the minutes range from `0` to `59`. However, to control exactly which minutes to display, the `minuteValues` input can take a number, an array of numbers, or a string of comma separated numbers. For example, if the minute selections should only be every 15 minutes, then this input value would be `minuteValues="0,15,30,45"`. | `number \| number[] \| string \| undefined` | `undefined` |
| `mode` | `mode` | The mode determines which platform styles to use. | `"ios" \| "md"` | `undefined` |
| `monthValues` | `month-values` | Values used to create the list of selectable months. By default the month values range from `1` to `12`. However, to control exactly which months to display, the `monthValues` input can take a number, an array of numbers, or a string of comma separated numbers. For example, if only summer months should be shown, then this input value would be `monthValues="6,7,8"`. Note that month numbers do *not* have a zero-based index, meaning January's value is `1`, and December's is `12`. | `number \| number[] \| string \| undefined` | `undefined` |
| `name` | `name` | The name of the control, which is submitted with the form data. | `string` | `this.inputId` |
| `presentation` | `presentation` | Which values you want to select. `'date'` will show a calendar picker to select the month, day, and year. `'time'` will show a time picker to select the hour, minute, and (optionally) AM/PM. `'date-time'` will show the date picker first and time picker second. `'time-date'` will show the time picker first and date picker second. | `"date" \| "date-time" \| "time" \| "time-date"` | `'date-time'` |
| `readonly` | `readonly` | If `true`, the datetime appears normal but is not interactive. | `boolean` | `false` |
| `showDefaultButtons` | `show-default-buttons` | If `true`, the default "Cancel" and "OK" buttons will be rendered at the bottom of the `ion-datetime` component. Developers can also use the `button` slot if they want to customize these buttons. If custom buttons are set in the `button` slot then the default buttons will not be rendered. | `boolean` | `false` |
| `showDefaultTimeLabel` | `show-default-time-label` | If `true`, the default "Time" label will be rendered for the time selector of the `ion-datetime` component. Developers can also use the `time-label` slot if they want to customize this label. If a custom label is set in the `time-label` slot then the default label will not be rendered. | `boolean` | `true` |
| `showDefaultTitle` | `show-default-title` | If `true`, a header will be shown above the calendar picker. On `ios` mode this will include the slotted title, and on `md` mode this will include the slotted title and the selected date. | `boolean` | `false` |
| `size` | `size` | If `cover`, the `ion-datetime` will expand to cover the full width of its container. If `fixed`, the `ion-datetime` will have a fixed width. | `"cover" \| "fixed"` | `'fixed'` |
| `value` | `value` | The value of the datetime as a valid ISO 8601 datetime string. | `null \| string \| undefined` | `undefined` |
| `yearValues` | `year-values` | Values used to create the list of selectable years. By default the year values range between the `min` and `max` datetime inputs. However, to control exactly which years to display, the `yearValues` input can take a number, an array of numbers, or string of comma separated numbers. For example, to show upcoming and recent leap years, then this input's value would be `yearValues="2024,2020,2016,2012,2008"`. | `number \| number[] \| string \| undefined` | `undefined` |
| Property | Attribute | Description | Type | Default |
| ---------------------- | ------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------- | -------------- |
| `cancelText` | `cancel-text` | The text to display on the picker's cancel button. | `string` | `'Cancel'` |
| `color` | `color` | The color to use from your application's color palette. Default options are: `"primary"`, `"secondary"`, `"tertiary"`, `"success"`, `"warning"`, `"danger"`, `"light"`, `"medium"`, and `"dark"`. For more information on colors, see [theming](/docs/theming/basics). | `string \| undefined` | `'primary'` |
| `dayValues` | `day-values` | Values used to create the list of selectable days. By default every day is shown for the given month. However, to control exactly which days of the month to display, the `dayValues` input can take a number, an array of numbers, or a string of comma separated numbers. Note that even if the array days have an invalid number for the selected month, like `31` in February, it will correctly not show days which are not valid for the selected month. | `number \| number[] \| string \| undefined` | `undefined` |
| `disabled` | `disabled` | If `true`, the user cannot interact with the datetime. | `boolean` | `false` |
| `doneText` | `done-text` | The text to display on the picker's "Done" button. | `string` | `'Done'` |
| `firstDayOfWeek` | `first-day-of-week` | The first day of the week to use for `ion-datetime`. The default value is `0` and represents Sunday. | `number` | `0` |
| `hourCycle` | `hour-cycle` | The hour cycle of the `ion-datetime`. If no value is set, this is specified by the current locale. | `"h12" \| "h23" \| undefined` | `undefined` |
| `hourValues` | `hour-values` | Values used to create the list of selectable hours. By default the hour values range from `0` to `23` for 24-hour, or `1` to `12` for 12-hour. However, to control exactly which hours to display, the `hourValues` input can take a number, an array of numbers, or a string of comma separated numbers. | `number \| number[] \| string \| undefined` | `undefined` |
| `locale` | `locale` | The locale to use for `ion-datetime`. This impacts month and day name formatting. The `'default'` value refers to the default locale set by your device. | `string` | `'default'` |
| `max` | `max` | The maximum datetime allowed. Value must be a date string following the [ISO 8601 datetime format standard](https://www.w3.org/TR/NOTE-datetime), `1996-12-19`. The format does not have to be specific to an exact datetime. For example, the maximum could just be the year, such as `1994`. Defaults to the end of this year. | `string \| undefined` | `undefined` |
| `min` | `min` | The minimum datetime allowed. Value must be a date string following the [ISO 8601 datetime format standard](https://www.w3.org/TR/NOTE-datetime), such as `1996-12-19`. The format does not have to be specific to an exact datetime. For example, the minimum could just be the year, such as `1994`. Defaults to the beginning of the year, 100 years ago from today. | `string \| undefined` | `undefined` |
| `minuteValues` | `minute-values` | Values used to create the list of selectable minutes. By default the minutes range from `0` to `59`. However, to control exactly which minutes to display, the `minuteValues` input can take a number, an array of numbers, or a string of comma separated numbers. For example, if the minute selections should only be every 15 minutes, then this input value would be `minuteValues="0,15,30,45"`. | `number \| number[] \| string \| undefined` | `undefined` |
| `mode` | `mode` | The mode determines which platform styles to use. | `"ios" \| "md"` | `undefined` |
| `monthValues` | `month-values` | Values used to create the list of selectable months. By default the month values range from `1` to `12`. However, to control exactly which months to display, the `monthValues` input can take a number, an array of numbers, or a string of comma separated numbers. For example, if only summer months should be shown, then this input value would be `monthValues="6,7,8"`. Note that month numbers do *not* have a zero-based index, meaning January's value is `1`, and December's is `12`. | `number \| number[] \| string \| undefined` | `undefined` |
| `name` | `name` | The name of the control, which is submitted with the form data. | `string` | `this.inputId` |
| `presentation` | `presentation` | Which values you want to select. `'date'` will show a calendar picker to select the month, day, and year. `'time'` will show a time picker to select the hour, minute, and (optionally) AM/PM. `'date-time'` will show the date picker first and time picker second. `'time-date'` will show the time picker first and date picker second. | `"date" \| "date-time" \| "month" \| "month-year" \| "time" \| "time-date" \| "year"` | `'date-time'` |
| `readonly` | `readonly` | If `true`, the datetime appears normal but is not interactive. | `boolean` | `false` |
| `showDefaultButtons` | `show-default-buttons` | If `true`, the default "Cancel" and "OK" buttons will be rendered at the bottom of the `ion-datetime` component. Developers can also use the `button` slot if they want to customize these buttons. If custom buttons are set in the `button` slot then the default buttons will not be rendered. | `boolean` | `false` |
| `showDefaultTimeLabel` | `show-default-time-label` | If `true`, the default "Time" label will be rendered for the time selector of the `ion-datetime` component. Developers can also use the `time-label` slot if they want to customize this label. If a custom label is set in the `time-label` slot then the default label will not be rendered. | `boolean` | `true` |
| `showDefaultTitle` | `show-default-title` | If `true`, a header will be shown above the calendar picker. On `ios` mode this will include the slotted title, and on `md` mode this will include the slotted title and the selected date. | `boolean` | `false` |
| `size` | `size` | If `cover`, the `ion-datetime` will expand to cover the full width of its container. If `fixed`, the `ion-datetime` will have a fixed width. | `"cover" \| "fixed"` | `'fixed'` |
| `value` | `value` | The value of the datetime as a valid ISO 8601 datetime string. | `null \| string \| undefined` | `undefined` |
| `yearValues` | `year-values` | Values used to create the list of selectable years. By default the year values range between the `min` and `max` datetime inputs. However, to control exactly which years to display, the `yearValues` input can take a number, an array of numbers, or string of comma separated numbers. For example, to show upcoming and recent leap years, then this input's value would be `yearValues="2024,2020,2016,2012,2008"`. | `number \| number[] \| string \| undefined` | `undefined` |
## Events

View File

@ -12,7 +12,7 @@
<style>
.grid {
display: grid;
grid-template-columns: repeat(2, minmax(250px, 1fr));
grid-template-columns: repeat(3, minmax(250px, 1fr));
grid-gap: 60px 20px;
}
h2 {
@ -71,6 +71,24 @@
presentation="date"
></ion-datetime>
</div>
<div class="grid-item">
<h2>month-year</h2>
<ion-datetime
presentation="month-year"
></ion-datetime>
</div>
<div class="grid-item">
<h2>month</h2>
<ion-datetime
presentation="month"
></ion-datetime>
</div>
<div class="grid-item">
<h2>year</h2>
<ion-datetime
presentation="year"
></ion-datetime>
</div>
</ion-content>
</ion-app>
</body>

View File

@ -77,17 +77,6 @@ export const isDayDisabled = (
return false;
}
export const getCalendarYearState = (refYear: number, activeParts: DatetimeParts, todayParts: DatetimeParts) => {
const isActiveYear = refYear === activeParts.year;
const isCurrentYear = refYear === todayParts.year;
return {
isActiveYear,
isCurrentYear,
ariaSelected: isActiveYear ? 'true' : null
}
}
/**
* Given a locale, a date, the selected date, and today's date,
* generate the state for a given calendar day button.