@ionic/core
Version:
Base components for Ionic
644 lines (638 loc) • 113 kB
JavaScript
/*!
* (C) Ionic http://ionicframework.com - MIT License
*/
import { proxyCustomElement, HTMLElement, createEvent, writeTask, h, Host } from '@stencil/core/internal/client';
import { startFocusVisible } from './focus-visible.js';
import { r as raf, e as renderHiddenInput, g as getElementRoot } from './helpers.js';
import { a as printIonError, p as printIonWarning } from './index6.js';
import { i as isRTL } from './dir.js';
import { c as createColorClasses } from './theme.js';
import { a as chevronBack, f as chevronForward, c as chevronDown, g as caretUpSharp, h as caretDownSharp } from './index7.js';
import { b as getIonMode } from './ionic-global.js';
import { g as generateDayAriaLabel, a as getDay, i as isBefore, b as isAfter, c as isSameDay, d as getPreviousMonth, e as getNextMonth, v as validateParts, f as getPartsFromCalendarDay, h as getNextYear, j as getPreviousYear, k as getEndOfWeek, l as getStartOfWeek, m as getPreviousDay, n as getNextDay, o as getPreviousWeek, p as getNextWeek, q as parseMinParts, r as parseMaxParts, s as parseDate, w as warnIfValueOutOfBounds, t as convertToArrayOfNumbers, u as convertDataToISO, x as getToday, y as getClosestValidDate, z as generateMonths, A as getNumDaysInMonth, B as getCombinedDateColumnData, C as getMonthColumnData, D as getDayColumnData, E as getYearColumnData, F as isMonthFirstLocale, G as getTimeColumnsData, H as isLocaleDayPeriodRTL, I as getMonthAndYear, J as getDaysOfWeek, K as getDaysOfMonth, L as getHourCycle, M as getLocalizedTime, N as getLocalizedDateTime, O as formatValue, P as clampDate, Q as parseAmPm, R as calculateHourFromAMPM } from './data.js';
import { d as defineCustomElement$a } from './backdrop.js';
import { d as defineCustomElement$9 } from './button.js';
import { d as defineCustomElement$8 } from './buttons.js';
import { d as defineCustomElement$7 } from './icon.js';
import { d as defineCustomElement$6 } from './picker.js';
import { d as defineCustomElement$5 } from './picker-column.js';
import { d as defineCustomElement$4 } from './picker-column-option.js';
import { d as defineCustomElement$3 } from './popover.js';
import { d as defineCustomElement$2 } from './ripple-effect.js';
const isYearDisabled = (refYear, minParts, maxParts) => {
if (minParts && minParts.year > refYear) {
return true;
}
if (maxParts && maxParts.year < refYear) {
return true;
}
return false;
};
/**
* Returns true if a given day should
* not be interactive according to its value,
* or the max/min dates.
*/
const isDayDisabled = (refParts, minParts, maxParts, dayValues) => {
/**
* If this is a filler date (i.e. padding)
* then the date is disabled.
*/
if (refParts.day === null) {
return true;
}
/**
* If user passed in a list of acceptable day values
* check to make sure that the date we are looking
* at is in this array.
*/
if (dayValues !== undefined && !dayValues.includes(refParts.day)) {
return true;
}
/**
* Given a min date, perform the following
* checks. If any of them are true, then the
* day should be disabled:
* 1. Is the current year < the min allowed year?
* 2. Is the current year === min allowed year,
* but the current month < the min allowed month?
* 3. Is the current year === min allowed year, the
* current month === min allow month, but the current
* day < the min allowed day?
*/
if (minParts && isBefore(refParts, minParts)) {
return true;
}
/**
* Given a max date, perform the following
* checks. If any of them are true, then the
* day should be disabled:
* 1. Is the current year > the max allowed year?
* 2. Is the current year === max allowed year,
* but the current month > the max allowed month?
* 3. Is the current year === max allowed year, the
* current month === max allow month, but the current
* day > the max allowed day?
*/
if (maxParts && isAfter(refParts, maxParts)) {
return true;
}
/**
* If none of these checks
* passed then the date should
* be interactive.
*/
return false;
};
/**
* Given a locale, a date, the selected date(s), and today's date,
* generate the state for a given calendar day button.
*/
const getCalendarDayState = (locale, refParts, activeParts, todayParts, minParts, maxParts, dayValues) => {
/**
* activeParts signals what day(s) are currently selected in the datetime.
* If multiple="true", this will be an array, but the logic in this util
* is the same whether we have one selected day or many because we're only
* calculating the state for one button. So, we treat a single activeParts value
* the same as an array of length one.
*/
const activePartsArray = Array.isArray(activeParts) ? activeParts : [activeParts];
/**
* The day button is active if it is selected, or in other words, if refParts
* matches at least one selected date.
*/
const isActive = activePartsArray.find((parts) => isSameDay(refParts, parts)) !== undefined;
const isToday = isSameDay(refParts, todayParts);
const disabled = isDayDisabled(refParts, minParts, maxParts, dayValues);
/**
* Note that we always return one object regardless of whether activeParts
* was an array, since we pare down to one value for isActive.
*/
return {
disabled,
isActive,
isToday,
ariaSelected: isActive ? 'true' : null,
ariaLabel: generateDayAriaLabel(locale, isToday, refParts),
text: refParts.day != null ? getDay(locale, refParts) : null,
};
};
/**
* Returns `true` if the month is disabled given the
* current date value and min/max date constraints.
*/
const isMonthDisabled = (refParts, { minParts, maxParts, }) => {
// If the year is disabled then the month is disabled.
if (isYearDisabled(refParts.year, minParts, maxParts)) {
return true;
}
// If the date value is before the min date, then the month is disabled.
// If the date value is after the max date, then the month is disabled.
if ((minParts && isBefore(refParts, minParts)) || (maxParts && isAfter(refParts, maxParts))) {
return true;
}
return false;
};
/**
* Given a working date, an optional minimum date range,
* and an optional maximum date range; determine if the
* previous navigation button is disabled.
*/
const isPrevMonthDisabled = (refParts, minParts, maxParts) => {
const prevMonth = Object.assign(Object.assign({}, getPreviousMonth(refParts)), { day: null });
return isMonthDisabled(prevMonth, {
minParts,
maxParts,
});
};
/**
* Given a working date and a maximum date range,
* determine if the next navigation button is disabled.
*/
const isNextMonthDisabled = (refParts, maxParts) => {
const nextMonth = Object.assign(Object.assign({}, getNextMonth(refParts)), { day: null });
return isMonthDisabled(nextMonth, {
maxParts,
});
};
/**
* Given the value of the highlightedDates property
* and an ISO string, return the styles to use for
* that date, or undefined if none are found.
*/
const getHighlightStyles = (highlightedDates, dateIsoString, el) => {
if (Array.isArray(highlightedDates)) {
const dateStringWithoutTime = dateIsoString.split('T')[0];
const matchingHighlight = highlightedDates.find((hd) => hd.date === dateStringWithoutTime);
if (matchingHighlight) {
return {
textColor: matchingHighlight.textColor,
backgroundColor: matchingHighlight.backgroundColor,
};
}
}
else {
/**
* Wrap in a try-catch to prevent exceptions in the user's function
* from interrupting the calendar's rendering.
*/
try {
return highlightedDates(dateIsoString);
}
catch (e) {
printIonError('Exception thrown from provided `highlightedDates` callback. Please check your function and try again.', el, e);
}
}
return undefined;
};
/**
* If a time zone is provided in the format options, the rendered text could
* differ from what was selected in the Datetime, which could cause
* confusion.
*/
const warnIfTimeZoneProvided = (el, formatOptions) => {
var _a, _b, _c, _d;
if (((_a = formatOptions === null || formatOptions === void 0 ? void 0 : formatOptions.date) === null || _a === void 0 ? void 0 : _a.timeZone) ||
((_b = formatOptions === null || formatOptions === void 0 ? void 0 : formatOptions.date) === null || _b === void 0 ? void 0 : _b.timeZoneName) ||
((_c = formatOptions === null || formatOptions === void 0 ? void 0 : formatOptions.time) === null || _c === void 0 ? void 0 : _c.timeZone) ||
((_d = formatOptions === null || formatOptions === void 0 ? void 0 : formatOptions.time) === null || _d === void 0 ? void 0 : _d.timeZoneName)) {
printIonWarning('Datetime: "timeZone" and "timeZoneName" are not supported in "formatOptions".', el);
}
};
const checkForPresentationFormatMismatch = (el, presentation, formatOptions) => {
// formatOptions is not required
if (!formatOptions)
return;
// If formatOptions is provided, the date and/or time objects are required, depending on the presentation
switch (presentation) {
case 'date':
case 'month-year':
case 'month':
case 'year':
if (formatOptions.date === undefined) {
printIonWarning(`Datetime: The '${presentation}' presentation requires a date object in formatOptions.`, el);
}
break;
case 'time':
if (formatOptions.time === undefined) {
printIonWarning(`Datetime: The 'time' presentation requires a time object in formatOptions.`, el);
}
break;
case 'date-time':
case 'time-date':
if (formatOptions.date === undefined && formatOptions.time === undefined) {
printIonWarning(`Datetime: The '${presentation}' presentation requires either a date or time object (or both) in formatOptions.`, el);
}
break;
}
};
const datetimeIosCss = ":host{display:-ms-flexbox;display:flex;-ms-flex-flow:column;flex-flow:column;background:var(--background);overflow:hidden}:host(.datetime-size-fixed){width:auto;height:auto}:host(.datetime-size-fixed:not(.datetime-prefer-wheel)){max-width:350px}:host(.datetime-size-fixed.datetime-prefer-wheel){min-width:350px;max-width:-webkit-max-content;max-width:-moz-max-content;max-width:max-content}:host(.datetime-size-cover){width:100%}:host .calendar-body,:host .datetime-year{opacity:0}:host(:not(.datetime-ready)) .datetime-year{position:absolute;pointer-events:none}:host(.datetime-ready) .calendar-body{opacity:1}:host(.datetime-ready) .datetime-year{display:none;opacity:1}:host .wheel-order-year-first .day-column{-ms-flex-order:3;order:3;text-align:end}:host .wheel-order-year-first .month-column{-ms-flex-order:2;order:2;text-align:end}:host .wheel-order-year-first .year-column{-ms-flex-order:1;order:1;text-align:start}:host .datetime-calendar,:host .datetime-year{display:-ms-flexbox;display:flex;-ms-flex:1 1 auto;flex:1 1 auto;-ms-flex-flow:column;flex-flow:column}:host(.show-month-and-year) .datetime-year{display:-ms-flexbox;display:flex}:host(.show-month-and-year) .calendar-next-prev,:host(.show-month-and-year) .calendar-days-of-week,:host(.show-month-and-year) .calendar-body,:host(.show-month-and-year) .datetime-time{display:none}:host(.month-year-picker-open) .datetime-footer{display:none}:host(.datetime-disabled){pointer-events:none}:host(.datetime-disabled) .calendar-days-of-week,:host(.datetime-disabled) .datetime-time{opacity:0.4}:host(.datetime-readonly){pointer-events:none;}:host(.datetime-readonly) .calendar-action-buttons,:host(.datetime-readonly) .calendar-body,:host(.datetime-readonly) .datetime-year{pointer-events:initial}:host(.datetime-readonly) .calendar-day[disabled]:not(.calendar-day-constrained),:host(.datetime-readonly) .datetime-action-buttons ion-button[disabled]{opacity:1}:host .datetime-header .datetime-title{text-overflow:ellipsis;white-space:nowrap;overflow:hidden}:host .datetime-action-buttons.has-clear-button{width:100%}:host .datetime-action-buttons ion-buttons{display:-ms-flexbox;display:flex;-ms-flex-pack:justify;justify-content:space-between}.datetime-action-buttons .datetime-action-buttons-container{display:-ms-flexbox;display:flex}:host .calendar-action-buttons{display:-ms-flexbox;display:flex;-ms-flex-pack:justify;justify-content:space-between}:host .calendar-action-buttons ion-button{--background:transparent}:host .calendar-days-of-week{display:grid;grid-template-columns:repeat(7, 1fr);text-align:center}.calendar-days-of-week .day-of-week{-webkit-margin-start:auto;margin-inline-start:auto;-webkit-margin-end:auto;margin-inline-end:auto;margin-top:0;margin-bottom:0}:host .calendar-body{display:-ms-flexbox;display:flex;-ms-flex-positive:1;flex-grow:1;-webkit-scroll-snap-type:x mandatory;-ms-scroll-snap-type:x mandatory;scroll-snap-type:x mandatory;overflow-x:scroll;overflow-y:hidden;scrollbar-width:none;outline:none}:host .calendar-body .calendar-month{display:-ms-flexbox;display:flex;-ms-flex-flow:column;flex-flow:column;scroll-snap-align:start;scroll-snap-stop:always;-ms-flex-negative:0;flex-shrink:0;width:100%}:host .calendar-body .calendar-month-disabled{scroll-snap-align:none}:host .calendar-body::-webkit-scrollbar{display:none}:host .calendar-body .calendar-month-grid{display:grid;grid-template-columns:repeat(7, 1fr)}:host .calendar-day-wrapper{display:-ms-flexbox;display:flex;-ms-flex-align:center;align-items:center;-ms-flex-pack:center;justify-content:center;min-width:0;min-height:0;overflow:visible}.calendar-day{border-radius:50%;-webkit-padding-start:0px;padding-inline-start:0px;-webkit-padding-end:0px;padding-inline-end:0px;padding-top:0px;padding-bottom:0px;-webkit-margin-start:0px;margin-inline-start:0px;-webkit-margin-end:0px;margin-inline-end:0px;margin-top:0px;margin-bottom:0px;display:-ms-flexbox;display:flex;position:relative;-ms-flex-align:center;align-items:center;-ms-flex-pack:center;justify-content:center;border:none;outline:none;background:none;color:currentColor;font-family:var(--ion-font-family, inherit);cursor:pointer;-webkit-appearance:none;-moz-appearance:none;appearance:none;z-index:0}:host .calendar-day[disabled]{pointer-events:none;opacity:0.4}.calendar-day:focus{background:rgba(var(--ion-color-base-rgb), 0.2);-webkit-box-shadow:0px 0px 0px 4px rgba(var(--ion-color-base-rgb), 0.2);box-shadow:0px 0px 0px 4px rgba(var(--ion-color-base-rgb), 0.2)}:host .datetime-time{display:-ms-flexbox;display:flex;-ms-flex-pack:justify;justify-content:space-between}:host(.datetime-presentation-time) .datetime-time{padding-left:0;padding-right:0;padding-top:0;padding-bottom:0}:host ion-popover{--height:200px}:host .time-header{display:-ms-flexbox;display:flex;-ms-flex-align:center;align-items:center}:host .time-body{border-radius:8px;-webkit-padding-start:12px;padding-inline-start:12px;-webkit-padding-end:12px;padding-inline-end:12px;padding-top:6px;padding-bottom:6px;display:-ms-flexbox;display:flex;border:none;background:var(--ion-color-step-300, var(--ion-background-color-step-300, #edeef0));color:var(--ion-text-color, #000);font-family:inherit;font-size:inherit;cursor:pointer;-webkit-appearance:none;-moz-appearance:none;appearance:none}:host .time-body-active{color:var(--ion-color-base)}:host(.in-item){position:static}:host(.show-month-and-year) .calendar-action-buttons .calendar-month-year-toggle{color:var(--ion-color-base)}.calendar-month-year{min-width:0}.calendar-month-year-toggle{font-family:inherit;font-size:inherit;font-style:inherit;font-weight:inherit;letter-spacing:inherit;text-decoration:inherit;text-indent:inherit;text-overflow:inherit;text-transform:inherit;text-align:inherit;white-space:inherit;color:inherit;position:relative;border:0;outline:none;background:transparent;cursor:pointer;z-index:1}.calendar-month-year-toggle::after{left:0;right:0;top:0;bottom:0;position:absolute;content:\"\";opacity:0;-webkit-transition:opacity 15ms linear, background-color 15ms linear;transition:opacity 15ms linear, background-color 15ms linear;z-index:-1}.calendar-month-year-toggle.ion-focused::after{background:currentColor}.calendar-month-year-toggle:disabled{opacity:0.3;pointer-events:none}.calendar-month-year-toggle ion-icon{-webkit-padding-start:4px;padding-inline-start:4px;-webkit-padding-end:0;padding-inline-end:0;padding-top:0;padding-bottom:0;-ms-flex-negative:0;flex-shrink:0}.calendar-month-year-toggle #toggle-wrapper{display:-ms-inline-flexbox;display:inline-flex;-ms-flex-align:center;align-items:center}ion-picker{--highlight-background:var(--wheel-highlight-background);--highlight-border-radius:var(--wheel-highlight-border-radius);--fade-background-rgb:var(--wheel-fade-background-rgb)}:host{--background:var(--ion-color-light, #f4f5f8);--background-rgb:var(--ion-color-light-rgb, 244, 245, 248);--title-color:var(--ion-color-step-600, var(--ion-text-color-step-400, #666666))}:host(.datetime-presentation-date-time:not(.datetime-prefer-wheel)),:host(.datetime-presentation-time-date:not(.datetime-prefer-wheel)),:host(.datetime-presentation-date:not(.datetime-prefer-wheel)){min-height:350px}:host .datetime-header{-webkit-padding-start:16px;padding-inline-start:16px;-webkit-padding-end:16px;padding-inline-end:16px;padding-top:16px;padding-bottom:16px;border-bottom:0.55px solid var(--ion-color-step-200, var(--ion-background-color-step-200, #cccccc));font-size:min(0.875rem, 22.4px)}:host .datetime-header .datetime-title{color:var(--title-color)}:host .datetime-header .datetime-selected-date{margin-top:10px}.calendar-month-year-toggle{-webkit-padding-start:16px;padding-inline-start:16px;-webkit-padding-end:16px;padding-inline-end:16px;padding-top:0px;padding-bottom:0px;min-height:44px;font-size:min(1rem, 25.6px);font-weight:600}.calendar-month-year-toggle.ion-focused::after{opacity:0.15}.calendar-month-year-toggle #toggle-wrapper{-webkit-margin-start:0;margin-inline-start:0;-webkit-margin-end:8px;margin-inline-end:8px;margin-top:10px;margin-bottom:10px}:host .calendar-action-buttons .calendar-month-year-toggle ion-icon,:host .calendar-action-buttons ion-buttons ion-button{color:var(--ion-color-base)}:host .calendar-action-buttons ion-buttons{padding-left:0;padding-right:0;padding-top:8px;padding-bottom:0}:host .calendar-action-buttons ion-buttons ion-button{margin-left:0;margin-right:0;margin-top:0;margin-bottom:0}:host .calendar-days-of-week{-webkit-padding-start:8px;padding-inline-start:8px;-webkit-padding-end:8px;padding-inline-end:8px;padding-top:0;padding-bottom:0;color:var(--ion-color-step-300, var(--ion-text-color-step-700, #b3b3b3));font-size:min(0.75rem, 19.2px);font-weight:600;line-height:24px;text-transform:uppercase}@supports (border-radius: mod(1px, 1px)){.calendar-days-of-week .day-of-week{width:clamp(20px, calc(mod(min(1rem, 24px), 24px) * 10), 100%);height:24px;overflow:hidden}.calendar-day{border-radius:max(8px, mod(min(1rem, 24px), 24px) * 10)}}@supports ((border-radius: mod(1px, 1px)) and (background: -webkit-named-image(apple-pay-logo-black)) and (not (contain-intrinsic-size: none))) or (not (border-radius: mod(1px, 1px))){.calendar-days-of-week .day-of-week{width:auto;height:auto;overflow:initial}.calendar-day{border-radius:32px}}:host .calendar-body .calendar-month .calendar-month-grid{-webkit-padding-start:8px;padding-inline-start:8px;-webkit-padding-end:8px;padding-inline-end:8px;padding-top:8px;padding-bottom:8px;-ms-flex-align:center;align-items:center;height:calc(100% - 16px)}:host .calendar-day-wrapper{-webkit-padding-start:4px;padding-inline-start:4px;-webkit-padding-end:4px;padding-inline-end:4px;padding-top:4px;padding-bottom:4px;height:0;min-height:1rem}:host .calendar-day{width:40px;min-width:40px;height:40px;font-size:min(1.25rem, 32px)}.calendar-day.calendar-day-active{background:rgba(var(--ion-color-base-rgb), 0.2);font-size:min(1.375rem, 35.2px)}:host .calendar-day.calendar-day-today{color:var(--ion-color-base)}:host .calendar-day.calendar-day-active{color:var(--ion-color-base);font-weight:600}:host .calendar-day.calendar-day-today.calendar-day-active{background:var(--ion-color-base);color:var(--ion-color-contrast)}:host .datetime-time{-webkit-padding-start:16px;padding-inline-start:16px;-webkit-padding-end:16px;padding-inline-end:16px;padding-top:8px;padding-bottom:16px;font-size:min(1rem, 25.6px)}:host .datetime-time .time-header{font-weight:600}:host .datetime-buttons{-webkit-padding-start:8px;padding-inline-start:8px;-webkit-padding-end:8px;padding-inline-end:8px;padding-top:8px;padding-bottom:8px;border-top:0.55px solid var(--ion-color-step-200, var(--ion-background-color-step-200, #cccccc))}:host .datetime-buttons ::slotted(ion-buttons),:host .datetime-buttons ion-buttons{display:-ms-flexbox;display:flex;-ms-flex-align:center;align-items:center;-ms-flex-pack:justify;justify-content:space-between}:host .datetime-action-buttons{width:100%}";
const IonDatetimeIosStyle0 = datetimeIosCss;
const datetimeMdCss = ":host{display:-ms-flexbox;display:flex;-ms-flex-flow:column;flex-flow:column;background:var(--background);overflow:hidden}:host(.datetime-size-fixed){width:auto;height:auto}:host(.datetime-size-fixed:not(.datetime-prefer-wheel)){max-width:350px}:host(.datetime-size-fixed.datetime-prefer-wheel){min-width:350px;max-width:-webkit-max-content;max-width:-moz-max-content;max-width:max-content}:host(.datetime-size-cover){width:100%}:host .calendar-body,:host .datetime-year{opacity:0}:host(:not(.datetime-ready)) .datetime-year{position:absolute;pointer-events:none}:host(.datetime-ready) .calendar-body{opacity:1}:host(.datetime-ready) .datetime-year{display:none;opacity:1}:host .wheel-order-year-first .day-column{-ms-flex-order:3;order:3;text-align:end}:host .wheel-order-year-first .month-column{-ms-flex-order:2;order:2;text-align:end}:host .wheel-order-year-first .year-column{-ms-flex-order:1;order:1;text-align:start}:host .datetime-calendar,:host .datetime-year{display:-ms-flexbox;display:flex;-ms-flex:1 1 auto;flex:1 1 auto;-ms-flex-flow:column;flex-flow:column}:host(.show-month-and-year) .datetime-year{display:-ms-flexbox;display:flex}:host(.show-month-and-year) .calendar-next-prev,:host(.show-month-and-year) .calendar-days-of-week,:host(.show-month-and-year) .calendar-body,:host(.show-month-and-year) .datetime-time{display:none}:host(.month-year-picker-open) .datetime-footer{display:none}:host(.datetime-disabled){pointer-events:none}:host(.datetime-disabled) .calendar-days-of-week,:host(.datetime-disabled) .datetime-time{opacity:0.4}:host(.datetime-readonly){pointer-events:none;}:host(.datetime-readonly) .calendar-action-buttons,:host(.datetime-readonly) .calendar-body,:host(.datetime-readonly) .datetime-year{pointer-events:initial}:host(.datetime-readonly) .calendar-day[disabled]:not(.calendar-day-constrained),:host(.datetime-readonly) .datetime-action-buttons ion-button[disabled]{opacity:1}:host .datetime-header .datetime-title{text-overflow:ellipsis;white-space:nowrap;overflow:hidden}:host .datetime-action-buttons.has-clear-button{width:100%}:host .datetime-action-buttons ion-buttons{display:-ms-flexbox;display:flex;-ms-flex-pack:justify;justify-content:space-between}.datetime-action-buttons .datetime-action-buttons-container{display:-ms-flexbox;display:flex}:host .calendar-action-buttons{display:-ms-flexbox;display:flex;-ms-flex-pack:justify;justify-content:space-between}:host .calendar-action-buttons ion-button{--background:transparent}:host .calendar-days-of-week{display:grid;grid-template-columns:repeat(7, 1fr);text-align:center}.calendar-days-of-week .day-of-week{-webkit-margin-start:auto;margin-inline-start:auto;-webkit-margin-end:auto;margin-inline-end:auto;margin-top:0;margin-bottom:0}:host .calendar-body{display:-ms-flexbox;display:flex;-ms-flex-positive:1;flex-grow:1;-webkit-scroll-snap-type:x mandatory;-ms-scroll-snap-type:x mandatory;scroll-snap-type:x mandatory;overflow-x:scroll;overflow-y:hidden;scrollbar-width:none;outline:none}:host .calendar-body .calendar-month{display:-ms-flexbox;display:flex;-ms-flex-flow:column;flex-flow:column;scroll-snap-align:start;scroll-snap-stop:always;-ms-flex-negative:0;flex-shrink:0;width:100%}:host .calendar-body .calendar-month-disabled{scroll-snap-align:none}:host .calendar-body::-webkit-scrollbar{display:none}:host .calendar-body .calendar-month-grid{display:grid;grid-template-columns:repeat(7, 1fr)}:host .calendar-day-wrapper{display:-ms-flexbox;display:flex;-ms-flex-align:center;align-items:center;-ms-flex-pack:center;justify-content:center;min-width:0;min-height:0;overflow:visible}.calendar-day{border-radius:50%;-webkit-padding-start:0px;padding-inline-start:0px;-webkit-padding-end:0px;padding-inline-end:0px;padding-top:0px;padding-bottom:0px;-webkit-margin-start:0px;margin-inline-start:0px;-webkit-margin-end:0px;margin-inline-end:0px;margin-top:0px;margin-bottom:0px;display:-ms-flexbox;display:flex;position:relative;-ms-flex-align:center;align-items:center;-ms-flex-pack:center;justify-content:center;border:none;outline:none;background:none;color:currentColor;font-family:var(--ion-font-family, inherit);cursor:pointer;-webkit-appearance:none;-moz-appearance:none;appearance:none;z-index:0}:host .calendar-day[disabled]{pointer-events:none;opacity:0.4}.calendar-day:focus{background:rgba(var(--ion-color-base-rgb), 0.2);-webkit-box-shadow:0px 0px 0px 4px rgba(var(--ion-color-base-rgb), 0.2);box-shadow:0px 0px 0px 4px rgba(var(--ion-color-base-rgb), 0.2)}:host .datetime-time{display:-ms-flexbox;display:flex;-ms-flex-pack:justify;justify-content:space-between}:host(.datetime-presentation-time) .datetime-time{padding-left:0;padding-right:0;padding-top:0;padding-bottom:0}:host ion-popover{--height:200px}:host .time-header{display:-ms-flexbox;display:flex;-ms-flex-align:center;align-items:center}:host .time-body{border-radius:8px;-webkit-padding-start:12px;padding-inline-start:12px;-webkit-padding-end:12px;padding-inline-end:12px;padding-top:6px;padding-bottom:6px;display:-ms-flexbox;display:flex;border:none;background:var(--ion-color-step-300, var(--ion-background-color-step-300, #edeef0));color:var(--ion-text-color, #000);font-family:inherit;font-size:inherit;cursor:pointer;-webkit-appearance:none;-moz-appearance:none;appearance:none}:host .time-body-active{color:var(--ion-color-base)}:host(.in-item){position:static}:host(.show-month-and-year) .calendar-action-buttons .calendar-month-year-toggle{color:var(--ion-color-base)}.calendar-month-year{min-width:0}.calendar-month-year-toggle{font-family:inherit;font-size:inherit;font-style:inherit;font-weight:inherit;letter-spacing:inherit;text-decoration:inherit;text-indent:inherit;text-overflow:inherit;text-transform:inherit;text-align:inherit;white-space:inherit;color:inherit;position:relative;border:0;outline:none;background:transparent;cursor:pointer;z-index:1}.calendar-month-year-toggle::after{left:0;right:0;top:0;bottom:0;position:absolute;content:\"\";opacity:0;-webkit-transition:opacity 15ms linear, background-color 15ms linear;transition:opacity 15ms linear, background-color 15ms linear;z-index:-1}.calendar-month-year-toggle.ion-focused::after{background:currentColor}.calendar-month-year-toggle:disabled{opacity:0.3;pointer-events:none}.calendar-month-year-toggle ion-icon{-webkit-padding-start:4px;padding-inline-start:4px;-webkit-padding-end:0;padding-inline-end:0;padding-top:0;padding-bottom:0;-ms-flex-negative:0;flex-shrink:0}.calendar-month-year-toggle #toggle-wrapper{display:-ms-inline-flexbox;display:inline-flex;-ms-flex-align:center;align-items:center}ion-picker{--highlight-background:var(--wheel-highlight-background);--highlight-border-radius:var(--wheel-highlight-border-radius);--fade-background-rgb:var(--wheel-fade-background-rgb)}:host{--background:var(--ion-color-step-100, var(--ion-background-color-step-100, #ffffff));--title-color:var(--ion-color-contrast)}:host .datetime-header{-webkit-padding-start:20px;padding-inline-start:20px;-webkit-padding-end:20px;padding-inline-end:20px;padding-top:20px;padding-bottom:20px;background:var(--ion-color-base);color:var(--title-color)}:host .datetime-header .datetime-title{font-size:0.75rem;text-transform:uppercase}:host .datetime-header .datetime-selected-date{margin-top:30px;font-size:2.125rem}:host .calendar-action-buttons ion-button{--color:var(--ion-color-step-650, var(--ion-text-color-step-350, #595959))}.calendar-month-year-toggle{-webkit-padding-start:20px;padding-inline-start:20px;-webkit-padding-end:16px;padding-inline-end:16px;padding-top:12px;padding-bottom:12px;min-height:48px;background:transparent;color:var(--ion-color-step-650, var(--ion-text-color-step-350, #595959));z-index:1}.calendar-month-year-toggle.ion-focused::after{opacity:0.04}.calendar-month-year-toggle ion-ripple-effect{color:currentColor}@media (any-hover: hover){.calendar-month-year-toggle.ion-activatable:not(.ion-focused):hover::after{background:currentColor;opacity:0.04}}:host .calendar-days-of-week{-webkit-padding-start:10px;padding-inline-start:10px;-webkit-padding-end:10px;padding-inline-end:10px;padding-top:0px;padding-bottom:0px;color:var(--ion-color-step-500, var(--ion-text-color-step-500, gray));font-size:0.875rem;line-height:36px}:host .calendar-body .calendar-month .calendar-month-grid{-webkit-padding-start:10px;padding-inline-start:10px;-webkit-padding-end:10px;padding-inline-end:10px;padding-top:4px;padding-bottom:4px;grid-template-rows:repeat(6, 1fr)}:host .calendar-day{width:42px;min-width:42px;height:42px;font-size:0.875rem}:host .calendar-day.calendar-day-today{border:1px solid var(--ion-color-base);color:var(--ion-color-base)}:host .calendar-day.calendar-day-active{color:var(--ion-color-contrast)}.calendar-day.calendar-day-active{border:1px solid var(--ion-color-base);background:var(--ion-color-base)}:host .datetime-time{-webkit-padding-start:16px;padding-inline-start:16px;-webkit-padding-end:16px;padding-inline-end:16px;padding-top:8px;padding-bottom:8px}:host .time-header{color:var(--ion-color-step-650, var(--ion-text-color-step-350, #595959))}:host(.datetime-presentation-month) .datetime-year,:host(.datetime-presentation-year) .datetime-year,:host(.datetime-presentation-month-year) .datetime-year{margin-top:20px;margin-bottom:20px}:host .datetime-buttons{-webkit-padding-start:10px;padding-inline-start:10px;-webkit-padding-end:10px;padding-inline-end:10px;padding-top:10px;padding-bottom:10px;display:-ms-flexbox;display:flex;-ms-flex-align:center;align-items:center;-ms-flex-pack:end;justify-content:flex-end}";
const IonDatetimeMdStyle0 = datetimeMdCss;
const Datetime = /*@__PURE__*/ proxyCustomElement(class Datetime extends HTMLElement {
constructor() {
super();
this.__registerHost();
this.__attachShadow();
this.ionCancel = createEvent(this, "ionCancel", 7);
this.ionChange = createEvent(this, "ionChange", 7);
this.ionValueChange = createEvent(this, "ionValueChange", 7);
this.ionFocus = createEvent(this, "ionFocus", 7);
this.ionBlur = createEvent(this, "ionBlur", 7);
this.ionStyle = createEvent(this, "ionStyle", 7);
this.ionRender = createEvent(this, "ionRender", 7);
this.inputId = `ion-dt-${datetimeIds++}`;
this.prevPresentation = null;
this.warnIfIncorrectValueUsage = () => {
const { multiple, value } = this;
if (!multiple && Array.isArray(value)) {
/**
* We do some processing on the `value` array so
* that it looks more like an array when logged to
* the console.
* Example given ['a', 'b']
* Default toString() behavior: a,b
* Custom behavior: ['a', 'b']
*/
printIonWarning(`ion-datetime was passed an array of values, but multiple="false". This is incorrect usage and may result in unexpected behaviors. To dismiss this warning, pass a string to the "value" property when multiple="false".
Value Passed: [${value.map((v) => `'${v}'`).join(', ')}]
`, this.el);
}
};
this.setValue = (value) => {
this.value = value;
this.ionChange.emit({ value });
};
/**
* Returns the DatetimePart interface
* to use when rendering an initial set of
* data. This should be used when rendering an
* interface in an environment where the `value`
* may not be set. This function works
* by returning the first selected date and then
* falling back to defaultParts if no active date
* is selected.
*/
this.getActivePartsWithFallback = () => {
var _a;
const { defaultParts } = this;
return (_a = this.getActivePart()) !== null && _a !== void 0 ? _a : defaultParts;
};
this.getActivePart = () => {
const { activeParts } = this;
return Array.isArray(activeParts) ? activeParts[0] : activeParts;
};
this.closeParentOverlay = (role) => {
const popoverOrModal = this.el.closest('ion-modal, ion-popover');
if (popoverOrModal) {
popoverOrModal.dismiss(undefined, role);
}
};
this.setWorkingParts = (parts) => {
this.workingParts = Object.assign({}, parts);
};
this.setActiveParts = (parts, removeDate = false) => {
/** if the datetime component is in readonly mode,
* allow browsing of the calendar without changing
* the set value
*/
if (this.readonly) {
return;
}
const { multiple, minParts, maxParts, activeParts } = this;
/**
* When setting the active parts, it is possible
* to set invalid data. For example,
* when updating January 31 to February,
* February 31 does not exist. As a result
* we need to validate the active parts and
* ensure that we are only setting valid dates.
* Additionally, we need to update the working parts
* too in the event that the validated parts are different.
*/
const validatedParts = validateParts(parts, minParts, maxParts);
this.setWorkingParts(validatedParts);
if (multiple) {
const activePartsArray = Array.isArray(activeParts) ? activeParts : [activeParts];
if (removeDate) {
this.activeParts = activePartsArray.filter((p) => !isSameDay(p, validatedParts));
}
else {
this.activeParts = [...activePartsArray, validatedParts];
}
}
else {
this.activeParts = Object.assign({}, validatedParts);
}
const hasSlottedButtons = this.el.querySelector('[slot="buttons"]') !== null;
if (hasSlottedButtons || this.showDefaultButtons) {
return;
}
this.confirm();
};
this.initializeKeyboardListeners = () => {
const calendarBodyRef = this.calendarBodyRef;
if (!calendarBodyRef) {
return;
}
const root = this.el.shadowRoot;
/**
* Get a reference to the month
* element we are currently viewing.
*/
const currentMonth = calendarBodyRef.querySelector('.calendar-month:nth-of-type(2)');
/**
* When focusing the calendar body, we want to pass focus
* to the working day, but other days should
* only be accessible using the arrow keys. Pressing
* Tab should jump between bodies of selectable content.
*/
const checkCalendarBodyFocus = (ev) => {
var _a;
const record = ev[0];
/**
* If calendar body was already focused
* when this fired or if the calendar body
* if not currently focused, we should not re-focus
* the inner day.
*/
if (((_a = record.oldValue) === null || _a === void 0 ? void 0 : _a.includes('ion-focused')) || !calendarBodyRef.classList.contains('ion-focused')) {
return;
}
this.focusWorkingDay(currentMonth);
};
const mo = new MutationObserver(checkCalendarBodyFocus);
mo.observe(calendarBodyRef, { attributeFilter: ['class'], attributeOldValue: true });
this.destroyKeyboardMO = () => {
mo === null || mo === void 0 ? void 0 : mo.disconnect();
};
/**
* We must use keydown not keyup as we want
* to prevent scrolling when using the arrow keys.
*/
calendarBodyRef.addEventListener('keydown', (ev) => {
const activeElement = root.activeElement;
if (!activeElement || !activeElement.classList.contains('calendar-day')) {
return;
}
const parts = getPartsFromCalendarDay(activeElement);
let partsToFocus;
switch (ev.key) {
case 'ArrowDown':
ev.preventDefault();
partsToFocus = getNextWeek(parts);
break;
case 'ArrowUp':
ev.preventDefault();
partsToFocus = getPreviousWeek(parts);
break;
case 'ArrowRight':
ev.preventDefault();
partsToFocus = getNextDay(parts);
break;
case 'ArrowLeft':
ev.preventDefault();
partsToFocus = getPreviousDay(parts);
break;
case 'Home':
ev.preventDefault();
partsToFocus = getStartOfWeek(parts);
break;
case 'End':
ev.preventDefault();
partsToFocus = getEndOfWeek(parts);
break;
case 'PageUp':
ev.preventDefault();
partsToFocus = ev.shiftKey ? getPreviousYear(parts) : getPreviousMonth(parts);
break;
case 'PageDown':
ev.preventDefault();
partsToFocus = ev.shiftKey ? getNextYear(parts) : getNextMonth(parts);
break;
/**
* Do not preventDefault here
* as we do not want to override other
* browser defaults such as pressing Enter/Space
* to select a day.
*/
default:
return;
}
/**
* If the day we want to move focus to is
* disabled, do not do anything.
*/
if (isDayDisabled(partsToFocus, this.minParts, this.maxParts)) {
return;
}
this.setWorkingParts(Object.assign(Object.assign({}, this.workingParts), partsToFocus));
/**
* Give view a chance to re-render
* then move focus to the new working day
*/
requestAnimationFrame(() => this.focusWorkingDay(currentMonth));
});
};
this.focusWorkingDay = (currentMonth) => {
/**
* Get the number of padding days so
* we know how much to offset our next selector by
* to grab the correct calendar-day element.
*/
const padding = currentMonth.querySelectorAll('.calendar-day-padding');
const { day } = this.workingParts;
if (day === null) {
return;
}
/**
* Get the calendar day element
* and focus it.
*/
const dayEl = currentMonth.querySelector(`.calendar-day-wrapper:nth-of-type(${padding.length + day}) .calendar-day`);
if (dayEl) {
dayEl.focus();
}
};
this.processMinParts = () => {
const { min, defaultParts } = this;
if (min === undefined) {
this.minParts = undefined;
return;
}
this.minParts = parseMinParts(min, defaultParts);
};
this.processMaxParts = () => {
const { max, defaultParts } = this;
if (max === undefined) {
this.maxParts = undefined;
return;
}
this.maxParts = parseMaxParts(max, defaultParts);
};
this.initializeCalendarListener = () => {
const calendarBodyRef = this.calendarBodyRef;
if (!calendarBodyRef) {
return;
}
/**
* For performance reasons, we only render 3
* months at a time: The current month, the previous
* month, and the next month. We have a scroll listener
* on the calendar body to append/prepend new months.
*
* We can do this because Stencil is smart enough to not
* re-create the .calendar-month containers, but rather
* update the content within those containers.
*
* As an added bonus, WebKit has some troubles with
* scroll-snap-stop: always, so not rendering all of
* the months in a row allows us to mostly sidestep
* that issue.
*/
const months = calendarBodyRef.querySelectorAll('.calendar-month');
const startMonth = months[0];
const workingMonth = months[1];
const endMonth = months[2];
const mode = getIonMode(this);
const needsiOSRubberBandFix = mode === 'ios' && typeof navigator !== 'undefined' && navigator.maxTouchPoints > 1;
/**
* Before setting up the scroll listener,
* scroll the middle month into view.
* scrollIntoView() will scroll entire page
* if element is not in viewport. Use scrollLeft instead.
*/
writeTask(() => {
calendarBodyRef.scrollLeft = startMonth.clientWidth * (isRTL(this.el) ? -1 : 1);
const getChangedMonth = (parts) => {
const box = calendarBodyRef.getBoundingClientRect();
/**
* If the current scroll position is all the way to the left
* then we have scrolled to the previous month.
* Otherwise, assume that we have scrolled to the next
* month. We have a tolerance of 2px to account for
* sub pixel rendering.
*
* Check below the next line ensures that we did not
* swipe and abort (i.e. we swiped but we are still on the current month).
*/
const condition = isRTL(this.el) ? calendarBodyRef.scrollLeft >= -2 : calendarBodyRef.scrollLeft <= 2;
const month = condition ? startMonth : endMonth;
/**
* The edge of the month must be lined up with
* the edge of the calendar body in order for
* the component to update. Otherwise, it
* may be the case that the user has paused their
* swipe or the browser has not finished snapping yet.
* Rather than check if the x values are equal,
* we give it a tolerance of 2px to account for
* sub pixel rendering.
*/
const monthBox = month.getBoundingClientRect();
if (Math.abs(monthBox.x - box.x) > 2)
return;
/**
* If we're force-rendering a month, assume we've
* scrolled to that and return it.
*
* If forceRenderDate is ever used in a context where the
* forced month is not immediately auto-scrolled to, this
* should be updated to also check whether `month` has the
* same month and year as the forced date.
*/
const { forceRenderDate } = this;
if (forceRenderDate !== undefined) {
return { month: forceRenderDate.month, year: forceRenderDate.year, day: forceRenderDate.day };
}
/**
* From here, we can determine if the start
* month or the end month was scrolled into view.
* If no month was changed, then we can return from
* the scroll callback early.
*/
if (month === startMonth) {
return getPreviousMonth(parts);
}
else if (month === endMonth) {
return getNextMonth(parts);
}
else {
return;
}
};
const updateActiveMonth = () => {
if (needsiOSRubberBandFix) {
calendarBodyRef.style.removeProperty('pointer-events');
appliediOSRubberBandFix = false;
}
/**
* If the month did not change
* then we can return early.
*/
const newDate = getChangedMonth(this.workingParts);
if (!newDate)
return;
const { month, day, year } = newDate;
if (isMonthDisabled({ month, year, day: null }, {
minParts: Object.assign(Object.assign({}, this.minParts), { day: null }),
maxParts: Object.assign(Object.assign({}, this.maxParts), { day: null }),
})) {
return;
}
/**
* Prevent scrolling for other browsers
* to give the DOM time to update and the container
* time to properly snap.
*/
calendarBodyRef.style.setProperty('overflow', 'hidden');
/**
* Use a writeTask here to ensure
* that the state is updated and the
* correct month is scrolled into view
* in the same frame. This is not
* typically a problem on newer devices
* but older/slower device may have a flicker
* if we did not do this.
*/
writeTask(() => {
this.setWorkingParts(Object.assign(Object.assign({}, this.workingParts), { month, day: day, year }));
calendarBodyRef.scrollLeft = workingMonth.clientWidth * (isRTL(this.el) ? -1 : 1);
calendarBodyRef.style.removeProperty('overflow');
if (this.resolveForceDateScrolling) {
this.resolveForceDateScrolling();
}
});
};
/**
* When the container finishes scrolling we
* need to update the DOM with the selected month.
*/
let scrollTimeout;
/**
* We do not want to attempt to set pointer-events
* multiple times within a single swipe gesture as
* that adds unnecessary work to the main thread.
*/
let appliediOSRubberBandFix = false;
const scrollCallback = () => {
if (scrollTimeout) {
clearTimeout(scrollTimeout);
}
/**
* On iOS it is possible to quickly rubber band
* the scroll area before the scroll timeout has fired.
* This results in users reaching the end of the scrollable
* container before the DOM has updated.
* By setting `pointer-events: none` we can ensure that
* subsequent swipes do not happen while the container
* is snapping.
*/
if (!appliediOSRubberBandFix && needsiOSRubberBandFix) {
calendarBodyRef.style.setProperty('pointer-events', 'none');
appliediOSRubberBandFix = true;
}
// Wait ~3 frames
scrollTimeout = setTimeout(updateActiveMonth, 50);
};
calendarBodyRef.addEventListen