UNPKG

@zoff-tech/zt-bottom-drawer

Version:
904 lines (897 loc) 86.9 kB
'use strict'; Object.defineProperty(exports, '__esModule', { value: true }); const index = require('./index-d7fcb92f.js'); const focusVisible = require('@utils/focus-visible'); const helpers = require('@utils/helpers'); const logging = require('@utils/logging'); const rtl = require('@utils/rtl'); const theme = require('@utils/theme'); const index$1 = require('./index-bc077cdf.js'); const ionicGlobal = require('./ionic-global-70a62cb2.js'); const data = require('./data-9d7463ab.js'); /*! * (C) Ionic http://ionicframework.com - MIT License */ 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 && data.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 && data.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) => data.isSameDay(refParts, parts)) !== undefined; const isToday = data.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: data.generateDayAriaLabel(locale, isToday, refParts), text: refParts.day != null ? data.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 && data.isBefore(refParts, minParts)) || (maxParts && data.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({}, data.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({}, data.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) { logging.printIonError('Exception thrown from provided `highlightedDates` callback. Please check your function and try again.', el, e); } } return undefined; }; const datetimeIosCss = ":host{display:flex;flex-flow:column;background:var(--background);overflow:hidden}ion-picker-column-internal{min-width:26px}: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: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{order:3;text-align:end}:host .wheel-order-year-first .month-column{order:2;text-align:end}:host .wheel-order-year-first .year-column{order:1;text-align:start}:host .datetime-calendar,:host .datetime-year{display:flex;flex:1 1 auto;flex-flow:column}:host(.show-month-and-year) .datetime-year{display:flex}@supports (background: -webkit-named-image(apple-pay-logo-black)) and (not (aspect-ratio: 1/1)){: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{position:absolute;visibility:hidden;pointer-events:none}@supports (inset-inline-start: 0){: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{inset-inline-start:-99999px}}@supports not (inset-inline-start: 0){: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{left:-99999px}:host-context([dir=rtl]):host(.show-month-and-year) .calendar-next-prev,:host-context([dir=rtl]).show-month-and-year .calendar-next-prev,:host-context([dir=rtl]):host(.show-month-and-year) .calendar-days-of-week,:host-context([dir=rtl]).show-month-and-year .calendar-days-of-week,:host-context([dir=rtl]):host(.show-month-and-year) .calendar-body,:host-context([dir=rtl]).show-month-and-year .calendar-body,:host-context([dir=rtl]):host(.show-month-and-year) .datetime-time,:host-context([dir=rtl]).show-month-and-year .datetime-time{left:unset;right:unset;right:-99999px}@supports selector(:dir(rtl)){:host(.show-month-and-year) .calendar-next-prev:dir(rtl),:host(.show-month-and-year) .calendar-days-of-week:dir(rtl),:host(.show-month-and-year) .calendar-body:dir(rtl),:host(.show-month-and-year) .datetime-time:dir(rtl){left:unset;right:unset;right:-99999px}}}}@supports (not (background: -webkit-named-image(apple-pay-logo-black))) or ((background: -webkit-named-image(apple-pay-logo-black)) and (aspect-ratio: 1/1)){: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-readonly),:host(.datetime-disabled){pointer-events:none}:host(.datetime-disabled){opacity:0.4}: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:flex;justify-content:space-between}:host .calendar-action-buttons{display:flex;justify-content:space-between}:host .calendar-action-buttons ion-item,:host .calendar-action-buttons ion-button{--background:translucent}:host .calendar-action-buttons ion-item ion-label{display:flex;align-items:center}:host .calendar-action-buttons ion-item ion-icon{-webkit-padding-start:4px;padding-inline-start:4px;-webkit-padding-end:0;padding-inline-end:0;padding-top:0;padding-bottom:0}:host .calendar-days-of-week{display:grid;grid-template-columns:repeat(7, 1fr);text-align:center}:host .calendar-body{display:flex;flex-grow:1;scroll-snap-type:x mandatory;overflow-x:scroll;overflow-y:hidden;scrollbar-width:none;outline:none}:host .calendar-body .calendar-month{scroll-snap-align:start;scroll-snap-stop:always;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{-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:flex;position:relative;align-items:center;justify-content:center;border:none;outline:none;background:none;color:currentColor;font-family:var(--ion-font-family, inherit);cursor:pointer;appearance:none;z-index:0}:host .calendar-day[disabled]{pointer-events:none;opacity:0.4}.calendar-day-highlight{border-radius:32px;-webkit-padding-start:4px;padding-inline-start:4px;-webkit-padding-end:4px;padding-inline-end:4px;padding-top:4px;padding-bottom:4px;position:absolute;width:32px;height:32px;z-index:-1}:host .datetime-time{display:flex;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:flex;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:flex;border:none;background:var(--ion-color-step-300, #edeef0);color:var(--ion-text-color, #000);font-family:inherit;font-size:inherit;cursor:pointer;appearance:none}:host .time-body-active{color:var(--ion-color-base)}:host(.in-item){position:static}:host(.show-month-and-year) .calendar-action-buttons ion-item{--color:var(--ion-color-base)}:host{--background:var(--ion-color-light, #ffffff);--background-rgb:var(--ion-color-light-rgb);--title-color:var(--ion-color-step-600, #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, #cccccc);font-size:14px}:host .datetime-header .datetime-title{color:var(--title-color)}:host .datetime-header .datetime-selected-date{margin-top:10px}:host .calendar-action-buttons ion-item{--padding-start:16px;--background-hover:transparent;--background-activated:transparent;font-size:16px;font-weight:600}:host .calendar-action-buttons ion-item 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, #b3b3b3);font-size:12px;font-weight:600;line-height:24px;text-transform:uppercase}: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;height:calc(100% - 16px)}:host .calendar-day{font-size:20px}.calendar-day:focus .calendar-day-highlight,.calendar-day.calendar-day-active .calendar-day-highlight{opacity:0.2}.calendar-day.calendar-day-active .calendar-day-highlight{background:var(--ion-color-base)}.calendar-day:focus .calendar-day-highlight{background:var(--ion-color-base) !important}: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{color:var(--ion-color-contrast)}.calendar-day.calendar-day-today.calendar-day-active .calendar-day-highlight{background:var(--ion-color-base);opacity:1}: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:16px}: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, #cccccc)}:host .datetime-buttons ::slotted(ion-buttons),:host .datetime-buttons ion-buttons{display:flex;align-items:center;justify-content:space-between}:host .datetime-action-buttons{width:100%}"; const datetimeMdCss = ":host{display:flex;flex-flow:column;background:var(--background);overflow:hidden}ion-picker-column-internal{min-width:26px}: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: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{order:3;text-align:end}:host .wheel-order-year-first .month-column{order:2;text-align:end}:host .wheel-order-year-first .year-column{order:1;text-align:start}:host .datetime-calendar,:host .datetime-year{display:flex;flex:1 1 auto;flex-flow:column}:host(.show-month-and-year) .datetime-year{display:flex}@supports (background: -webkit-named-image(apple-pay-logo-black)) and (not (aspect-ratio: 1/1)){: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{position:absolute;visibility:hidden;pointer-events:none}@supports (inset-inline-start: 0){: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{inset-inline-start:-99999px}}@supports not (inset-inline-start: 0){: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{left:-99999px}:host-context([dir=rtl]):host(.show-month-and-year) .calendar-next-prev,:host-context([dir=rtl]).show-month-and-year .calendar-next-prev,:host-context([dir=rtl]):host(.show-month-and-year) .calendar-days-of-week,:host-context([dir=rtl]).show-month-and-year .calendar-days-of-week,:host-context([dir=rtl]):host(.show-month-and-year) .calendar-body,:host-context([dir=rtl]).show-month-and-year .calendar-body,:host-context([dir=rtl]):host(.show-month-and-year) .datetime-time,:host-context([dir=rtl]).show-month-and-year .datetime-time{left:unset;right:unset;right:-99999px}@supports selector(:dir(rtl)){:host(.show-month-and-year) .calendar-next-prev:dir(rtl),:host(.show-month-and-year) .calendar-days-of-week:dir(rtl),:host(.show-month-and-year) .calendar-body:dir(rtl),:host(.show-month-and-year) .datetime-time:dir(rtl){left:unset;right:unset;right:-99999px}}}}@supports (not (background: -webkit-named-image(apple-pay-logo-black))) or ((background: -webkit-named-image(apple-pay-logo-black)) and (aspect-ratio: 1/1)){: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-readonly),:host(.datetime-disabled){pointer-events:none}:host(.datetime-disabled){opacity:0.4}: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:flex;justify-content:space-between}:host .calendar-action-buttons{display:flex;justify-content:space-between}:host .calendar-action-buttons ion-item,:host .calendar-action-buttons ion-button{--background:translucent}:host .calendar-action-buttons ion-item ion-label{display:flex;align-items:center}:host .calendar-action-buttons ion-item ion-icon{-webkit-padding-start:4px;padding-inline-start:4px;-webkit-padding-end:0;padding-inline-end:0;padding-top:0;padding-bottom:0}:host .calendar-days-of-week{display:grid;grid-template-columns:repeat(7, 1fr);text-align:center}:host .calendar-body{display:flex;flex-grow:1;scroll-snap-type:x mandatory;overflow-x:scroll;overflow-y:hidden;scrollbar-width:none;outline:none}:host .calendar-body .calendar-month{scroll-snap-align:start;scroll-snap-stop:always;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{-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:flex;position:relative;align-items:center;justify-content:center;border:none;outline:none;background:none;color:currentColor;font-family:var(--ion-font-family, inherit);cursor:pointer;appearance:none;z-index:0}:host .calendar-day[disabled]{pointer-events:none;opacity:0.4}.calendar-day-highlight{border-radius:32px;-webkit-padding-start:4px;padding-inline-start:4px;-webkit-padding-end:4px;padding-inline-end:4px;padding-top:4px;padding-bottom:4px;position:absolute;width:32px;height:32px;z-index:-1}:host .datetime-time{display:flex;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:flex;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:flex;border:none;background:var(--ion-color-step-300, #edeef0);color:var(--ion-text-color, #000);font-family:inherit;font-size:inherit;cursor:pointer;appearance:none}:host .time-body-active{color:var(--ion-color-base)}:host(.in-item){position:static}:host(.show-month-and-year) .calendar-action-buttons ion-item{--color:var(--ion-color-base)}:host{--background:var(--ion-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:12px;text-transform:uppercase}:host .datetime-header .datetime-selected-date{margin-top:30px;font-size:34px}:host .datetime-calendar .calendar-action-buttons ion-item{--padding-start:20px}:host .calendar-action-buttons ion-item,:host .calendar-action-buttons ion-button{--color:var(--ion-color-step-650, #595959)}: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, gray);font-size:14px;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{-webkit-padding-start:0px;padding-inline-start:0px;-webkit-padding-end:0;padding-inline-end:0;padding-top:13px;padding-bottom:13px;font-size:14px}.calendar-day:focus .calendar-day-highlight{background:rgba(var(--ion-color-base-rgb), 0.2);box-shadow:0px 0px 0px 4px rgba(var(--ion-color-base-rgb), 0.2)}:host .calendar-day.calendar-day-today{color:var(--ion-color-base)}.calendar-day.calendar-day-today .calendar-day-highlight{border:1px solid var(--ion-color-base)}:host .calendar-day.calendar-day-active{color:var(--ion-color-contrast)}.calendar-day.calendar-day-active .calendar-day-highlight{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, #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:flex;align-items:center;justify-content:flex-end}:host .datetime-view-buttons ion-button{color:var(--ion-color-step-800, #333333)}"; const Datetime = class { constructor(hostRef) { index.registerInstance(this, hostRef); this.ionCancel = index.createEvent(this, "ionCancel", 7); this.ionChange = index.createEvent(this, "ionChange", 7); this.ionValueChange = index.createEvent(this, "ionValueChange", 7); this.ionFocus = index.createEvent(this, "ionFocus", 7); this.ionBlur = index.createEvent(this, "ionBlur", 7); this.ionStyle = index.createEvent(this, "ionStyle", 7); this.ionRender = index.createEvent(this, "ionRender", 7); this.inputId = `ion-dt-${datetimeIds++}`; this.prevPresentation = null; /** * Duplicate reference to `activeParts` that does not trigger a re-render of the component. * Allows caching an instance of the `activeParts` in between render cycles. */ this.activePartsClone = []; 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'] */ logging.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 in * "activePartsClone" 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 { activePartsClone } = this; return Array.isArray(activePartsClone) ? activePartsClone[0] : activePartsClone; }; this.closeParentOverlay = () => { const popoverOrModal = this.el.closest('ion-modal, ion-popover'); if (popoverOrModal) { popoverOrModal.dismiss(); } }; this.setWorkingParts = (parts) => { this.workingParts = Object.assign({}, parts); }; this.setActiveParts = (parts, removeDate = false) => { const { multiple, minParts, maxParts, activePartsClone } = 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 = data.validateParts(parts, minParts, maxParts); this.setWorkingParts(validatedParts); if (multiple) { /** * We read from activePartsClone here because valueChanged() only updates that, * so it's the more reliable source of truth. If we read from activeParts, then * if you click July 1, manually set the value to July 2, and then click July 3, * the new value would be [July 1, July 3], ignoring the value set. * * We can then pass the new value to activeParts (rather than activePartsClone) * since the clone will be updated automatically by activePartsChanged(). */ const activePartsArray = Array.isArray(activePartsClone) ? activePartsClone : [activePartsClone]; if (removeDate) { this.activeParts = activePartsArray.filter((p) => !data.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 = data.getPartsFromCalendarDay(activeElement); let partsToFocus; switch (ev.key) { case 'ArrowDown': ev.preventDefault(); partsToFocus = data.getNextWeek(parts); break; case 'ArrowUp': ev.preventDefault(); partsToFocus = data.getPreviousWeek(parts); break; case 'ArrowRight': ev.preventDefault(); partsToFocus = data.getNextDay(parts); break; case 'ArrowLeft': ev.preventDefault(); partsToFocus = data.getPreviousDay(parts); break; case 'Home': ev.preventDefault(); partsToFocus = data.getStartOfWeek(parts); break; case 'End': ev.preventDefault(); partsToFocus = data.getEndOfWeek(parts); break; case 'PageUp': ev.preventDefault(); partsToFocus = ev.shiftKey ? data.getPreviousYear(parts) : data.getPreviousMonth(parts); break; case 'PageDown': ev.preventDefault(); partsToFocus = ev.shiftKey ? data.getNextYear(parts) : data.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 calenday-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:nth-of-type(${padding.length + day})`); if (dayEl) { dayEl.focus(); } }; this.processMinParts = () => { const { min, defaultParts } = this; if (min === undefined) { this.minParts = undefined; return; } this.minParts = data.parseMinParts(min, defaultParts); }; this.processMaxParts = () => { const { max, defaultParts } = this; if (max === undefined) { this.maxParts = undefined; return; } this.maxParts = data.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 = ionicGlobal.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. */ index.writeTask(() => { calendarBodyRef.scrollLeft = startMonth.clientWidth * (rtl.isRTL(this.el) ? -1 : 1); const getChangedMonth = (parts) => { const box = calendarBodyRef.getBoundingClientRect(); const root = this.el.shadowRoot; /** * Get the element that is in the center of the calendar body. * This will be an element inside of the active month. */ const elementAtCenter = root.elementFromPoint(box.x + box.width / 2, box.y + box.height / 2); /** * If there is no element then the * component may be re-rendering on a slow device. */ if (!elementAtCenter) return; const month = elementAtCenter.closest('.calendar-month'); if (!month) return; /** * 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; /** * 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 data.getPreviousMonth(parts); } else if (month === endMonth) { return data.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. */ index.writeTask(() => { this.setWorkingParts(Object.assign(Object.assign({}, this.workingParts), { month, day: day, year })); calendarBodyRef.scrollLeft = workingMonth.clientWidth * (rtl.isRTL(this.el) ? -1 : 1); calendarBodyRef.style.removeProperty('overflow'); }); }; /** * 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.addEventListener('scroll', scrollCallback); this.destroyCalendarListener = () => { calendarBodyRef.removeEventListener('scroll', scrollCallback); }; }); }; /** * Clean up all listeners except for the overlay * listener. This is so that we can re-create the listeners * if the datetime has been hidden/presented by a modal or popover. */ this.destroyInteractionListeners = () => { const { destroyCalendarListener, destroyKeyboardMO } = this; if (destroyCalendarListener !== undefined) { destroyCalendarListener(); } if (destroyKeyboardMO !== undefined) { destroyKeyboardMO(); } }; this.processValue = (value) => { const hasValue = value !== null && value !== undefined; const valueToProcess = hasValue ? data.parseDate(value) : this.defaultParts; const { minParts, maxParts } = this; this.warnIfIncorrectValueUsage(); /** * Datetime should only warn of out of bounds values * if set by the user. If the `value` is undefined, * we will default to today's date which may be out * of bounds. In this case, the warning makes it look * like the developer did something wrong which is * not true. */ if (hasValue) { data.warnIfValueOutOfBounds(valueToProcess, minParts, maxParts); } /** * If there are multiple values, pick an arbitrary one to clamp to. This way, * if the values are across months, we always show at least one of them. Note * that the values don't necessarily have to be in order. */ const singleValue = Array.isArray(valueToProcess) ? valueToProcess[0] : valueToProcess; const { month, day, year, hour, minute } = data.clampDate(singleValue, minParts, maxParts); const ampm = data.parseAmPm(hour); this.setWorkingParts({ month, day, year, hour, minute, ampm, }); /** * Since `activeParts` indicates a value that * been explicitly selected either by the * user or the app, only update `activeParts` * if the `value` property is set. */ if (hasValue) { if (Array.isArray(valueToProcess)) { this.activeParts = [...valueToProcess]; } else { this.activeParts = { month, day, year, hour, minute, ampm, }; } } else { /** * Reset the active parts if the value is not set. * This will clear the selected calendar day when * performing a clear action or using the reset() method. */ this.activeParts = []; } }; this.onFocus = () => { this.ionFocus.emit(); }; this.onBlur = () => { this.ionBlur.emit(); }; this.hasValue = () => { return this.value != null; }; this.nextMonth = () => { const calendarBodyRef = this.calendarBodyRef; if (!calendarBodyRef) { return; } const nextMonth = calendarBodyRef.querySelector('.calendar-month:last-of-type'); if (!nextMonth) { return; } const left = nextMonth.offsetWidth * 2; calendarBodyRef.scrollTo({ top: 0, left: left * (rtl.isRTL(this.el) ? -1 : 1), behavior: 'smooth', }); }; this.prevMonth = () => { const calendarBodyRef = this.calendarBodyRef; if (!calendarBodyRef) { return; } const prevMonth = calendarBodyRef.querySelector('.calendar-month:first-of-type'); if (!prevMonth) { return; } calendarBodyRef.scrollTo({ top: 0, left: 0, behavior: 'smooth', }); }; this.toggleMonthAndYearView = () => { this.showMonthAndYear = !this.showMonthAndYear; }; this.showMonthAndYear = false; this.activeParts = []; this.workingParts = { month: 5, day: 28, year: 2021, hour: 13, minute: 52, ampm: 'pm', }; this.isPresented = false; this.isTimePopoverOpen = false; this.color = 'primary'; this.name = this.inputId; this.disabled = false; this.readonly = false; this.isDateEnabled = undefined; this.min = undefined; this.max = undefined; this.presentation = 'date-time'; this.cancelText = 'Cancel'; this.doneText = 'Done'; this.clearText = 'Clear'; this.yearValues = undefined; this.monthValues = undefined; this.dayValues = undefined; this.hourValues = undefined; this.minuteValues = undefined; this.locale = 'default'; this.firstDayOfWeek = 0; this.titleSelectedDatesFormatter = undefined; this.multiple = false; this.highlightedDates = undefined; this.value = undefined; this.showDefaultTitle = false; this.showDefaultButtons = false; this.showClearButton = false; this.showDefaultTimeLabel = true; this.hourCycle = undefined; this.size = 'fixed'; this.preferWheel = false; } disabledChanged() { this.emitStyle(); } minChanged() { this.processMinParts(); } maxChanged() { this.processMaxParts(); } yearValuesChanged() { this.parsedYearValues = data.convertToArrayOfNumbers(this.yearValues); } monthValuesChanged() { this.parsedMonthValues = data.convertToArrayOfNumbers(this.monthValues); } dayValuesChanged() { this.parsedDayValues = data.convertToArrayOfNumbers(this.dayValues); } hourValuesChanged() { this.parsedHourValues = data.convertToArrayOfNumbers(this.hourValues); } minuteValuesChanged() { this.parsedMinuteValues = data.convertToArrayOfNumbers(this.minuteValues); } activePartsChanged() { this.activePartsClone = this.activeParts; } /** * Update the datetime value when the value changes */ valueChanged() { const { value, minParts, maxParts, workingParts } = this; if (this.hasValue()) { this.warnIfIncorrectValueUsage(); /** * Clones the value of the `activeParts` to the private clone, to update * the date display on the current render cycle without causing another render. * * This allows us to update the current value's date/time display without * refocusing or shifting the user's display (leaves the user in place). */ const valueDateParts = data.parseDate(value); if (valueDateParts) { data.warnIfValueOutOfBounds(valueDateParts, minParts, maxParts); if (Array.isArray(valueDateParts)) { this.activePartsClone = [...valueDateParts]; } else { const { month, day, year, hour, minute } = valueDateParts; const ampm = hour != null ? (hour >= 12 ? 'pm' : 'am') : undefined; this.activePartsClone = Object.assign(Object.assign({}, this.activeParts), { month, day, year, hour, minute, ampm }); /** * The working parts am/pm value must be updated when the value changes, to * ensure the time picker hour column values are generated correctly. * * Note that we don't need to do this if valueDateParts is an array, since * multiple="true" does not apply to time pickers. */ this.setWorkingParts(Object.assign(Object.assign({}, workingParts), { ampm })); } } else { logging.printIonWarning(`Unable to parse date string: ${value}. Please provide a valid ISO 8601 datetime string.`); } } this.emitStyle(); this.ionValueChange.emit({ value }); } /** * Confirms the selected datetime value, updates the * `value` property, and optionally closes the popover * or modal that the datetime was presented in. */ async confirm(closeOverlay = false) { const { isCalendarPicker, activeParts } = this; /** * We only update the value if the presentation is not a calendar picker. */ if (activeParts !== undefined || !isCalendarPicker) { const activePartsIsArray = Array.isArray(activeParts); if (activePartsIsArray && activeParts.length === 0) { this.setValue(undefined); } else { this.setValue(data.convertDataToISO(activeParts)); } } if (closeOverlay) { this.closeParentOverlay(); } } /** * Resets the internal state of the datetime but does not update the value. * Passing a valid ISO-8601 string will reset the state of the component to the provided date. * If no value is provided, the internal state will be reset to the clamped value of the min, max and today. */ async reset(startDate) { this.processValue(startDate); } /** * Emits the ionCancel event and * optionally closes the popover * or modal that the datetime was * presented in. */ async cancel(closeOverlay = false) { this.ionCancel.emit(); if (closeOverlay) { this.closeParentOverlay(); } } get isCalendarPicker() { const { presentation } = this; return presentation === 'date' || presentation === 'date-time' || presentation === 'time-date'; } connectedCallback() { this.clearFocusVisible = focusVisible.startFocusVisible(this.el).destroy; } disconnectedCallback() { if (this.clearFocusVisible) { this.clearFocusVisible(); this.clearFocusVisible = undefined; } } initializeListeners() { this.initializeCalendarListener(); this.initializeKeyboardListeners(); } componentDidLoad() { /** * If a scrollable element is hidden using `display: none`, * it will not have a scroll height meaning we cannot scroll elements * into view. As a result, we will need to wait for the datetime to become * visible if used inside of a modal or a popover otherwise the scrollable * areas will not have the correct values snapped into place. */ const visibleCallback = (entries) => { const ev = entries[0]; if (!ev.isIntersecting) { return; } this.initializeListeners(); /** * TODO FW-2793: Datetime needs a frame to ensure that it * can properly scroll contents into view. As a result * we hide