@sbh321/qcalendar
Version:
A forked version of Jeff Galbraith's fork of Quasar UI QCalendar
1,476 lines (1,314 loc) • 113 kB
JavaScript
/*!
* @subhambhandari/qcalendar v4.0.0-beta.19
* (c) 2024 Subham Bhandari <bhandarimaiya65@gmail.com>
* Released under the MIT License.
*/
import { reactive, ref, computed, withDirectives, h, watch, getCurrentInstance, onBeforeUnmount, defineComponent, onBeforeUpdate, onMounted, Transition } from 'vue';
const PARSE_REGEX = /^(\d{4})-(\d{1,2})(-(\d{1,2}))?([^\d]+(\d{1,2}))?(:(\d{1,2}))?(:(\d{1,2}))?(.(\d{1,3}))?$/;
const PARSE_DATE = /^(\d{4})-(\d{1,2})(-(\d{1,2}))/;
const PARSE_TIME = /(\d\d?)(:(\d\d?)|)(:(\d\d?)|)/;
const DAYS_IN_MONTH = [ 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 ];
const DAYS_IN_MONTH_LEAP = [ 0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 ];
const DAYS_IN_MONTH_MIN = 28;
const DAYS_IN_MONTH_MAX = 31;
const MONTH_MAX = 12;
const MONTH_MIN = 1;
const DAY_MIN = 1;
const DAYS_IN_WEEK = 7;
const MINUTES_IN_HOUR = 60;
const HOURS_IN_DAY = 24;
const FIRST_HOUR = 0;
const MILLISECONDS_IN_MINUTE = 60000;
const MILLISECONDS_IN_HOUR = 3600000;
const MILLISECONDS_IN_DAY = 86400000;
const MILLISECONDS_IN_WEEK = 604800000;
/* eslint-disable no-multi-spaces */
/**
* @typedef {Object} Timestamp The Timestamp object
* @property {string=} Timestamp.date Date string in format 'YYYY-MM-DD'
* @property {string=} Timestamp.time Time string in format 'HH:MM'
* @property {number} Timestamp.year The numeric year
* @property {number} Timestamp.month The numeric month (Jan = 1, ...)
* @property {number} Timestamp.day The numeric day
* @property {number} Timestamp.weekday The numeric weekday (Sun = 0, ..., Sat = 6)
* @property {number=} Timestamp.hour The numeric hour
* @property {number} Timestamp.minute The numeric minute
* @property {number=} Timestamp.doy The numeric day of the year (doy)
* @property {number=} Timestamp.workweek The numeric workweek
* @property {boolean} Timestamp.hasDay True if Timestamp.date is filled in and usable
* @property {boolean} Timestamp.hasTime True if Timestamp.time is filled in and usable
* @property {boolean=} Timestamp.past True if the Timestamp is in the past
* @property {boolean=} Timestamp.current True if Timestamp is current day (now)
* @property {boolean=} Timestamp.future True if Timestamp is in the future
* @property {boolean=} Timestamp.disabled True if this is a disabled date
* @property {boolean=} Timestamp.currentWeekday True if this date corresponds to current weekday
*/
const Timestamp = {
date: '', // YYYY-MM-DD
time: '', // HH:MM (optional)
year: 0, // YYYY
month: 0, // MM (Jan = 1, etc)
day: 0, // day of the month
weekday: 0, // week day (0=Sunday...6=Saturday)
hour: 0, // 24-hr format
minute: 0, // mm
doy: 0, // day of year
workweek: 0, // workweek number
hasDay: false, // if this timestamp is supposed to have a date
hasTime: false, // if this timestamp is supposed to have a time
past: false, // if timestamp is in the past (based on `now` property)
current: false, // if timestamp is current date (based on `now` property)
future: false, // if timestamp is in the future (based on `now` property)
disabled: false, // if timestamp is disabled
currentWeekday: false // if this date corresponds to current weekday
};
const TimeObject = {
hour: 0, // Number
minute: 0 // Number
};
/* eslint-enable no-multi-spaces */
// returns YYYY-MM-dd format
/**
* Returns today's date
* @returns {string} Date string in the form 'YYYY-MM-DD'
*/
function today () {
const d = new Date(),
month = '' + (d.getMonth() + 1),
day = '' + d.getDate(),
year = d.getFullYear();
return [ year, padNumber(month, 2), padNumber(day, 2) ].join('-')
}
/**
* Returns the start of the week give a {@link Timestamp} and weekdays (in which it finds the day representing the start of the week).
* If today {@link Timestamp} is passed in then this is used to update relative information in the returned {@link Timestamp}.
* @param {Timestamp} timestamp The {@link Timestamp} to use to find the start of the week
* @param {number[]} weekdays The array is [0,1,2,3,4,5,6] where 0=Sunday and 6=Saturday
* @param {Timestamp=} today If passed in then the {@link Timestamp} is updated with relative information
* @returns {Timestamp} The {@link Timestamp} representing the start of the week
*/
function getStartOfWeek (timestamp, weekdays, today) {
let start = copyTimestamp(timestamp);
if (start.day === 1 || start.weekday === 0) {
while (!weekdays.includes(start.weekday)) {
start = nextDay(start);
}
}
start = findWeekday(start, weekdays[ 0 ], prevDay);
start = updateFormatted(start);
if (today) {
start = updateRelative(start, today, start.hasTime);
}
return start
}
/**
* Returns the end of the week give a {@link Timestamp} and weekdays (in which it finds the day representing the last of the week).
* If today {@link Timestamp} is passed in then this is used to update relative information in the returned {@link Timestamp}.
* @param {Timestamp} timestamp The {@link Timestamp} to use to find the end of the week
* @param {number[]} weekdays The array is [0,1,2,3,4,5,6] where 0=Sunday and 6=Saturday
* @param {Timestamp=} today If passed in then the {@link Timestamp} is updated with relative information
* @returns {Timestamp} The {@link Timestamp} representing the end of the week
*/
function getEndOfWeek (timestamp, weekdays, today) {
let end = copyTimestamp(timestamp);
// is last day of month?
const lastDay = daysInMonth(end.year, end.month);
if (lastDay === end.day || end.weekday === 6) {
while (!weekdays.includes(end.weekday)) {
end = prevDay(end);
}
}
end = findWeekday(end, weekdays[ weekdays.length - 1 ], nextDay);
end = updateFormatted(end);
if (today) {
end = updateRelative(end, today, end.hasTime);
}
return end
}
/**
* Finds the start of the month based on the passed in {@link Timestamp}
* @param {Timestamp} timestamp The {@link Timestamp} to use to find the start of the month
* @returns {Timestamp} A {@link Timestamp} of the start of the month
*/
function getStartOfMonth (timestamp) {
const start = copyTimestamp(timestamp);
start.day = DAY_MIN;
updateFormatted(start);
return start
}
/**
* Finds the end of the month based on the passed in {@link Timestamp}
* @param {Timestamp} timestamp The {@link Timestamp} to use to find the end of the month
* @returns {Timestamp} A {@link Timestamp} of the end of the month
*/
function getEndOfMonth (timestamp) {
const end = copyTimestamp(timestamp);
end.day = daysInMonth(end.year, end.month);
updateFormatted(end);
return end
}
// returns minutes since midnight
function parseTime (input) {
const type = Object.prototype.toString.call(input);
switch (type) {
case '[object Number]':
// when a number is given, it's minutes since 12:00am
return input
case '[object String]':
{
// when a string is given, it's a hh:mm:ss format where seconds are optional, but not used
const parts = PARSE_TIME.exec(input);
if (!parts) {
return false
}
return parseInt(parts[ 1 ], 10) * 60 + parseInt(parts[ 3 ] || 0, 10)
}
case '[object Object]':
// when an object is given, it must have hour and minute
if (typeof input.hour !== 'number' || typeof input.minute !== 'number') {
return false
}
return input.hour * 60 + input.minute
}
return false
}
/**
* Validates the passed input ('YYY-MM-DD') as a date or ('YYY-MM-DD HH:MM') date time combination
* @param {string} input A string in the form 'YYYY-MM-DD' or 'YYYY-MM-DD HH:MM'
* @returns {boolean} True if parseable
*/
function validateTimestamp (input) {
return !!PARSE_REGEX.exec(input)
}
/**
* Compares two {@link Timestamp}s for exactness
* @param {Timestamp} ts1 The first {@link Timestamp}
* @param {Timestamp} ts2 The second {@link Timestamp}
* @returns {boolean} True if the two {@link Timestamp}s are an exact match
*/
function compareTimestamps (ts1, ts2) {
return JSON.stringify(ts1) === JSON.stringify(ts2)
}
/**
* Compares the date of two {@link Timestamp}s that have been updated with relative data
* @param {Timestamp} ts1 The first {@link Timestamp}
* @param {Timestamp} ts2 The second {@link Timestamp}
* @returns {boolean} True if the two dates are the same
*/
function compareDate (ts1, ts2) {
return getDate(ts1) === getDate(ts2)
}
/**
* Compares the time of two {@link Timestamp}s that have been updated with relative data
* @param {Timestamp} ts1 The first {@link Timestamp}
* @param {Timestamp} ts2 The second {@link Timestamp}
* @returns {boolean} True if the two times are an exact match
*/
function compareTime (ts1, ts2) {
return getTime(ts1) === getTime(ts2)
}
/**
* Compares the date and time of two {@link Timestamp}s that have been updated with relative data
* @param {Timestamp} ts1 The first {@link Timestamp}
* @param {Timestamp} ts2 The second {@link Timestamp}
* @returns {boolean} True if the date and time are an exact match
*/
function compareDateTime (ts1, ts2) {
return getDateTime(ts1) === getDateTime(ts2)
}
/**
* Fast low-level parser for a date string ('YYYY-MM-DD'). Does not update formatted or relative date.
* Use 'parseTimestamp' for formatted and relative updates
* @param {string} input In the form 'YYYY-MM-DD hh:mm:ss' (seconds are optional, but not used)
* @returns {Timestamp} This {@link Timestamp} is minimally filled in. The {@link Timestamp.date} and {@link Timestamp.time} as well as relative data will not be filled in.
*/
function parsed (input) {
const parts = PARSE_REGEX.exec(input);
if (!parts) return null
return {
date: input,
time: padNumber(parseInt(parts[ 6 ], 10) || 0, 2) + ':' + padNumber(parseInt(parts[ 8 ], 10) || 0, 2),
year: parseInt(parts[ 1 ], 10),
month: parseInt(parts[ 2 ], 10),
day: parseInt(parts[ 4 ], 10) || 1,
hour: !isNaN(parseInt(parts[ 6 ], 10)) ? parseInt(parts[ 6 ], 10) : 0,
minute: !isNaN(parseInt(parts[ 8 ], 10)) ? parseInt(parts[ 8 ], 10) : 0,
weekday: 0,
doy: 0,
workweek: 0,
hasDay: !!parts[ 4 ],
hasTime: true, // there is always time because no time is '00:00', which is valid
past: false,
current: false,
future: false,
disabled: false
}
}
/**
* High-level parser that converts the passed in string to {@link Timestamp} and uses 'now' to update relative information.
* @param {string} input In the form 'YYYY-MM-DD hh:mm:ss' (seconds are optional, but not used)
* @param {Timestamp} now A {@link Timestamp} to use for relative data updates
* @returns {Timestamp} The {@link Timestamp.date} will be filled in as well as the {@link Timestamp.time} if a time is supplied and formatted fields (doy, weekday, workweek, etc). If 'now' is supplied, then relative data will also be updated.
*/
function parseTimestamp (input, now) {
let timestamp = parsed(input);
if (timestamp === null) return null
timestamp = updateFormatted(timestamp);
if (now) {
updateRelative(timestamp, now, timestamp.hasTime);
}
return timestamp
}
/**
* Takes a JavaScript Date and returns a {@link Timestamp}. The {@link Timestamp} is not updated with relative information.
* @param {date} date JavaScript Date
* @param {boolean} utc If set the {@link Timestamp} will parse the Date as UTC
* @returns {Timestamp} A minimal {@link Timestamp} without updated or relative updates.
*/
function parseDate (date, utc = false) {
const UTC = !!utc ? 'UTC' : '';
return updateFormatted({
date: padNumber(date[ `get${ UTC }FullYear` ](), 4) + '-' + padNumber(date[ `get${ UTC }Month` ]() + 1, 2) + '-' + padNumber(date[ `get${ UTC }Date` ](), 2),
time: padNumber(date[ `get${ UTC }Hours` ]() || 0, 2) + ':' + padNumber(date[ `get${ UTC }Minutes` ]() || 0, 2),
year: date[ `get${ UTC }FullYear` ](),
month: date[ `get${ UTC }Month` ]() + 1,
day: date[ `get${ UTC }Date` ](),
hour: date[ `get${ UTC }Hours` ](),
minute: date[ `get${ UTC }Minutes` ](),
weekday: 0,
doy: 0,
workweek: 0,
hasDay: true,
hasTime: true, // Date always has time, even if it is '00:00'
past: false,
current: false,
future: false,
disabled: false
})
}
/**
* Converts a {@link Timestamp} into a numeric date identifier based on the passed {@link Timestamp}'s date
* @param {Timestamp} timestamp The {@link Timestamp} to use
* @returns {number} The numeric date identifier
*/
function getDayIdentifier (timestamp) {
return timestamp.year * 100000000 + timestamp.month * 1000000 + timestamp.day * 10000
}
/**
* Converts a {@link Timestamp} into a numeric time identifier based on the passed {@link Timestamp}'s time
* @param {Timestamp} timestamp The {@link Timestamp} to use
* @returns {number} The numeric time identifier
*/
function getTimeIdentifier (timestamp) {
return timestamp.hour * 100 + timestamp.minute
}
/**
* Converts a {@link Timestamp} into a numeric date and time identifier based on the passed {@link Timestamp}'s date and time
* @param {Timestamp} timestamp The {@link Timestamp} to use
* @returns {number} The numeric date+time identifier
*/
function getDayTimeIdentifier (timestamp) {
return getDayIdentifier(timestamp) + getTimeIdentifier(timestamp)
}
/**
* Returns the difference between two {@link Timestamp}s
* @param {Timestamp} ts1 The first {@link Timestamp}
* @param {Timestamp} ts2 The second {@link Timestamp}
* @param {boolean=} strict Optional flag to not to return negative numbers
* @returns {number} The difference
*/
function diffTimestamp (ts1, ts2, strict) {
const utc1 = Date.UTC(ts1.year, ts1.month - 1, ts1.day, ts1.hour, ts1.minute);
const utc2 = Date.UTC(ts2.year, ts2.month - 1, ts2.day, ts2.hour, ts2.minute);
if (strict === true && utc2 < utc1) {
// Not negative number
// utc2 - utc1 < 0 -> utc2 < utc1 -> NO: utc1 >= utc2
return 0
}
return utc2 - utc1
}
/**
* Updates a {@link Timestamp} with relative data (past, current and future)
* @param {Timestamp} timestamp The {@link Timestamp} that needs relative data updated
* @param {Timestamp} now {@link Timestamp} that represents the current date (optional time)
* @param {boolean=} time Optional flag to include time ('timestamp' and 'now' params should have time values)
* @returns {Timestamp} The updated {@link Timestamp}
*/
function updateRelative (timestamp, now, time = false) {
let a = getDayIdentifier(now);
let b = getDayIdentifier(timestamp);
let current = a === b;
if (timestamp.hasTime && time && current) {
a = getTimeIdentifier(now);
b = getTimeIdentifier(timestamp);
current = a === b;
}
timestamp.past = b < a;
timestamp.current = current;
timestamp.future = b > a;
timestamp.currentWeekday = timestamp.weekday === now.weekday;
return timestamp
}
/**
* Sets a Timestamp{@link Timestamp} to number of minutes past midnight (modifies hour and minutes if needed)
* @param {Timestamp} timestamp The {@link Timestamp} to modify
* @param {number} minutes The number of minutes to set from midnight
* @param {Timestamp=} now Optional {@link Timestamp} representing current date and time
* @returns {Timestamp} The updated {@link Timestamp}
*/
function updateMinutes (timestamp, minutes, now) {
timestamp.hasTime = true;
timestamp.hour = Math.floor(minutes / MINUTES_IN_HOUR);
timestamp.minute = minutes % MINUTES_IN_HOUR;
timestamp.time = getTime(timestamp);
if (now) {
updateRelative(timestamp, now, true);
}
return timestamp
}
/**
* Updates the {@link Timestamp} with the weekday
* @param {Timestamp} timestamp The {@link Timestamp} to modify
* @returns The modified Timestamp
*/
function updateWeekday (timestamp) {
timestamp.weekday = getWeekday(timestamp);
return timestamp
}
/**
* Updates the {@link Timestamp} with the day of the year (doy)
* @param {Timestamp} timestamp The {@link Timestamp} to modify
* @returns The modified Timestamp
*/
function updateDayOfYear (timestamp) {
timestamp.doy = getDayOfYear(timestamp);
return timestamp
}
/**
* Updates the {@link Timestamp} with the workweek
* @param {Timestamp} timestamp The {@link Timestamp} to modify
* @returns The modified {@link Timestamp}
*/
function updateWorkWeek (timestamp) {
timestamp.workweek = getWorkWeek(timestamp);
return timestamp
}
/**
* Updates the passed {@link Timestamp} with disabled, if needed
* @param {Timestamp} timestamp The {@link Timestamp} to modify
* @param {string} [disabledBefore] In 'YYY-MM-DD' format
* @param {string} [disabledAfter] In 'YYY-MM-DD' format
* @param {number[]} [disabledWeekdays] An array of numbers representing weekdays [0 = Sun, ..., 6 = Sat]
* @param {string[]} [disabledDays] An array of days in 'YYYY-MM-DD' format. If an array with a pair of dates is in first array, then this is treated as a range.
* @returns The modified {@link Timestamp}
*/
function updateDisabled (timestamp, disabledBefore, disabledAfter, disabledWeekdays, disabledDays) {
const t = getDayIdentifier(timestamp);
if (disabledBefore !== undefined) {
const before = getDayIdentifier(parsed(disabledBefore));
if (t <= before) {
timestamp.disabled = true;
}
}
if (timestamp.disabled !== true && disabledAfter !== undefined) {
const after = getDayIdentifier(parsed(disabledAfter));
if (t >= after) {
timestamp.disabled = true;
}
}
if (timestamp.disabled !== true && Array.isArray(disabledWeekdays) && disabledWeekdays.length > 0) {
for (const weekday in disabledWeekdays) {
if (disabledWeekdays[ weekday ] === timestamp.weekday) {
timestamp.disabled = true;
break
}
}
}
if (timestamp.disabled !== true && Array.isArray(disabledDays) && disabledDays.length > 0) {
for (const day in disabledDays) {
if (Array.isArray(disabledDays[ day ]) && disabledDays[ day ].length === 2) {
const start = parsed(disabledDays[ day ][ 0 ]);
const end = parsed(disabledDays[ day ][ 1 ]);
if (isBetweenDates(timestamp, start, end)) {
timestamp.disabled = true;
break
}
}
else {
const d = getDayIdentifier(parseTimestamp(disabledDays[ day ] + ' 00:00'));
if (d === t) {
timestamp.disabled = true;
break
}
}
}
}
return timestamp
}
/**
* Updates the passed {@link Timestamp} with formatted data (time string, date string, weekday, day of year and workweek)
* @param {Timestamp} timestamp The {@link Timestamp} to modify
* @returns The modified {@link Timestamp}
*/
function updateFormatted (timestamp) {
timestamp.hasTime = true;
timestamp.time = getTime(timestamp);
timestamp.date = getDate(timestamp);
timestamp.weekday = getWeekday(timestamp);
timestamp.doy = getDayOfYear(timestamp);
timestamp.workweek = getWorkWeek(timestamp);
return timestamp
}
/**
* Returns day of the year (doy) for the passed in {@link Timestamp}
* @param {Timestamp} timestamp The {@link Timestamp} to use
* @returns {number} The day of the year
*/
function getDayOfYear (timestamp) {
if (timestamp.year === 0) return
return (Date.UTC(timestamp.year, timestamp.month - 1, timestamp.day) - Date.UTC(timestamp.year, 0, 0)) / 24 / 60 / 60 / 1000
}
/**
* Returns workweek for the passed in {@link Timestamp}
* @param {Timestamp} timestamp The {@link Timestamp} to use
* @returns {number} The work week
*/
function getWorkWeek (timestamp) {
if (timestamp.year === 0) {
timestamp = parseTimestamp(today());
}
const date = makeDate(timestamp);
if (isNaN(date)) return 0
// Remove time components of date
const weekday = new Date(date.getFullYear(), date.getMonth(), date.getDate());
// Change date to Thursday same week
weekday.setDate(weekday.getDate() - ((weekday.getDay() + 6) % 7) + 3);
// Take January 4th as it is always in week 1 (see ISO 8601)
const firstThursday = new Date(weekday.getFullYear(), 0, 4);
// Change date to Thursday same week
firstThursday.setDate(firstThursday.getDate() - ((firstThursday.getDay() + 6) % 7) + 3);
// Check if daylight-saving-time-switch occurred and correct for it
const ds = weekday.getTimezoneOffset() - firstThursday.getTimezoneOffset();
weekday.setHours(weekday.getHours() - ds);
// Number of weeks between target Thursday and first Thursday
const weekDiff = (weekday - firstThursday) / (MILLISECONDS_IN_WEEK);
return 1 + Math.floor(weekDiff)
}
/**
* Returns weekday for the passed in {@link Timestamp}
* @param {Timestamp} timestamp The {@link Timestamp} to use
* @returns {number} The weekday
*/
function getWeekday (timestamp) {
let weekday = timestamp.weekday;
if (timestamp.hasDay) {
const floor = Math.floor;
const day = timestamp.day;
const month = ((timestamp.month + 9) % MONTH_MAX) + 1;
const century = floor(timestamp.year / 100);
const year = (timestamp.year % 100) - (timestamp.month <= 2 ? 1 : 0);
weekday = (((day + floor(2.6 * month - 0.2) - 2 * century + year + floor(year / 4) + floor(century / 4)) % 7) + 7) % 7;
}
return weekday
}
/**
* Returns if the passed year is a leap year
* @param {number} year The year to check (ie: 1999, 2020)
* @returns {boolean} True if the year is a leap year
*/
function isLeapYear (year) {
return ((year % 4 === 0) ^ (year % 100 === 0) ^ (year % 400 === 0)) === 1
}
/**
* Returns the days of the specified month in a year
* @param {number} year The year (ie: 1999, 2020)
* @param {number} month The month (zero-based)
* @returns {number} The number of days in the month (corrected for leap years)
*/
function daysInMonth (year, month) {
return isLeapYear(year) ? DAYS_IN_MONTH_LEAP[ month ] : DAYS_IN_MONTH[ month ]
}
/**
* Makes a copy of the passed in {@link Timestamp}
* @param {Timestamp} timestamp The original {@link Timestamp}
* @returns {Timestamp} A copy of the original {@link Timestamp}
*/
function copyTimestamp (timestamp) {
return { ...timestamp }
}
/**
* Padds a passed in number to length (converts to a string). Good for converting '5' as '05'.
* @param {number} x The number to pad
* @param {number} length The length of the required number as a string
* @returns {string} The padded number (as a string). (ie: 5 = '05')
*/
function padNumber (x, length) {
let padded = String(x);
while (padded.length < length) {
padded = '0' + padded;
}
return padded
}
/**
* Used internally to convert {@link Timestamp} used with 'parsed' or 'parseDate' so the 'date' portion of the {@link Timestamp} is correct.
* @param {Timestamp} timestamp The (raw) {@link Timestamp}
* @returns {string} A formatted date ('YYYY-MM-DD')
*/
function getDate (timestamp) {
let str = `${ padNumber(timestamp.year, 4) }-${ padNumber(timestamp.month, 2) }`;
if (timestamp.hasDay) str += `-${ padNumber(timestamp.day, 2) }`;
return str
}
/**
* Used intenally to convert {@link Timestamp} with 'parsed' or 'parseDate' so the 'time' portion of the {@link Timestamp} is correct.
* @param {Timestamp} timestamp The (raw) {@link Timestamp}
* @returns {string} A formatted time ('hh:mm')
*/
function getTime (timestamp) {
if (!timestamp.hasTime) {
return ''
}
return `${ padNumber(timestamp.hour, 2) }:${ padNumber(timestamp.minute, 2) }`
}
/**
* Returns a formatted string date and time ('YYYY-YY-MM hh:mm')
* @param {Timestamp} timestamp The {@link Timestamp}
* @returns {string} A formatted date time ('YYYY-MM-DD HH:mm')
*/
function getDateTime (timestamp) {
return getDate(timestamp) + ' ' + (timestamp.hasTime ? getTime(timestamp) : '00:00')
}
/**
* Returns a {@link Timestamp} of next day from passed in {@link Timestamp}
* @param {Timestamp} timestamp The {@link Timestamp} to use
* @returns {Timestamp} The modified {@link Timestamp} as the next day
*/
function nextDay (timestamp) {
++timestamp.day;
timestamp.weekday = (timestamp.weekday + 1) % DAYS_IN_WEEK;
if (timestamp.day > DAYS_IN_MONTH_MIN && timestamp.day > daysInMonth(timestamp.year, timestamp.month)) {
timestamp.day = DAY_MIN;
++timestamp.month;
if (timestamp.month > MONTH_MAX) {
timestamp.month = MONTH_MIN;
++timestamp.year;
}
}
return timestamp
}
/**
* Returns a {@link Timestamp} of previous day from passed in {@link Timestamp}
* @param {Timestamp} timestamp The {@link Timestamp} to use
* @returns {Timestamp} The modified {@link Timestamp} as the previous day
*/
function prevDay (timestamp) {
timestamp.day--;
timestamp.weekday = (timestamp.weekday + 6) % DAYS_IN_WEEK;
if (timestamp.day < DAY_MIN) {
timestamp.month--;
if (timestamp.month < MONTH_MIN) {
timestamp.year--;
timestamp.month = MONTH_MAX;
}
timestamp.day = daysInMonth(timestamp.year, timestamp.month);
}
return timestamp
}
/**
* An alias for {relativeDays}
* @param {Timestamp} timestamp The {@link Timestamp} to modify
* @param {function} [mover=nextDay] The mover function to use (ie: {nextDay} or {prevDay}).
* @param {number} [days=1] The number of days to move.
* @param {number[]} [allowedWeekdays=[ 0, 1, 2, 3, 4, 5, 6 ]] An array of numbers representing the weekdays. ie: [0 = Sun, ..., 6 = Sat].
* @returns The modified {@link Timestamp}
*/
function moveRelativeDays (timestamp, mover = nextDay, days = 1, allowedWeekdays = [ 0, 1, 2, 3, 4, 5, 6 ]) {
return relativeDays(timestamp, mover, days, allowedWeekdays)
}
/**
* Moves the {@link Timestamp} the number of relative days
* @param {Timestamp} timestamp The {@link Timestamp} to modify
* @param {function} [mover=nextDay] The mover function to use (ie: {nextDay} or {prevDay}).
* @param {number} [days=1] The number of days to move.
* @param {number[]} [allowedWeekdays=[ 0, 1, 2, 3, 4, 5, 6 ]] An array of numbers representing the weekdays. ie: [0 = Sun, ..., 6 = Sat].
* @returns The modified {@link Timestamp}
*/
function relativeDays (timestamp, mover = nextDay, days = 1, allowedWeekdays = [ 0, 1, 2, 3, 4, 5, 6 ]) {
if (!allowedWeekdays.includes(timestamp.weekday) && timestamp.weekday === 0 && mover === nextDay) {
++days;
}
while (--days >= 0) {
timestamp = mover(timestamp);
if (allowedWeekdays.length < 7 && !allowedWeekdays.includes(timestamp.weekday)) {
++days;
}
}
return timestamp
}
/**
* Finds the specified weekday (forward or back) based on the {@link Timestamp}
* @param {Timestamp} timestamp The {@link Timestamp} to modify
* @param {number} weekday The weekday number (Sun = 0, ..., Sat = 6)
* @param {function} [mover=nextDay] The function to use ({prevDay} or {nextDay}).
* @param {number} [maxDays=6] The number of days to look forward or back.
* @returns The modified {@link Timestamp}
*/
function findWeekday (timestamp, weekday, mover = nextDay, maxDays = 6) {
while (timestamp.weekday !== weekday && --maxDays >= 0) timestamp = mover(timestamp);
return timestamp
}
/**
* Returns an array of 0's and mostly 1's representing skipped days (used internally)
* @param {number[]} weekdays An array of numbers representing the weekdays. ie: [0 = Sun, ..., 6 = Sat].
* @returns {number[]} An array of 0's and mostly 1's (other numbers may occur previous to skipped days)
*/
function getWeekdaySkips (weekdays) {
const skips = [ 1, 1, 1, 1, 1, 1, 1 ];
const filled = [ 0, 0, 0, 0, 0, 0, 0 ];
for (let i = 0; i < weekdays.length; ++i) {
filled[ weekdays[ i ] ] = 1;
}
for (let k = 0; k < DAYS_IN_WEEK; ++k) {
let skip = 1;
for (let j = 1; j < DAYS_IN_WEEK; ++j) {
const next = (k + j) % DAYS_IN_WEEK;
if (filled[ next ]) {
break
}
++skip;
}
skips[ k ] = filled[ k ] * skip;
}
return skips
}
/**
* Creates an array of {@link Timestamp}s based on start and end params
* @param {Timestamp} start The starting {@link Timestamp}
* @param {Timestamp} end The ending {@link Timestamp}
* @param {Timestamp} now The relative day
* @param {number[]} weekdaySkips An array representing available and skipped weekdays
* @param {string} [disabledBefore] Days before this date are disabled (YYYY-MM-DD)
* @param {string} [disabledAfter] Days after this date are disabled (YYYY-MM-DD)
* @param {number[]} [disabledWeekdays] An array representing weekdays that are disabled [0 = Sun, ..., 6 = Sat]
* @param {string[]} [disabledDays] An array of days in 'YYYY-MM-DD' format. If an array with a pair of dates is in first array, then this is treated as a range.
* @param {number} [max=42] Max days to do
* @param {number} [min=0] Min days to do
* @returns {Timestamp[]} The requested array of {@link Timestamp}s
*/
function createDayList (start, end, now, weekdaySkips, disabledBefore, disabledAfter, disabledWeekdays = [], disabledDays = [], max = 42, min = 0) {
const stop = getDayIdentifier(end);
const days = [];
let current = copyTimestamp(start);
let currentIdentifier = 0;
let stopped = currentIdentifier === stop;
if (stop < getDayIdentifier(start)) {
return days
}
while ((!stopped || days.length < min) && days.length < max) {
currentIdentifier = getDayIdentifier(current);
stopped = stopped || (currentIdentifier > stop && days.length >= min);
if (stopped) {
break
}
if (weekdaySkips[ current.weekday ] === 0) {
current = relativeDays(current, nextDay);
continue
}
const day = copyTimestamp(current);
updateFormatted(day);
updateRelative(day, now);
updateDisabled(day, disabledBefore, disabledAfter, disabledWeekdays, disabledDays);
days.push(day);
current = relativeDays(current, nextDay);
}
return days
}
/**
* Creates an array of interval {@link Timestamp}s based on params
* @param {Timestamp} timestamp The starting {@link Timestamp}
* @param {number} first The starting interval time
* @param {number} minutes How many minutes between intervals (ie: 60, 30, 15 would be common ones)
* @param {number} count The number of intervals needed
* @param {Timestamp} now A relative {@link Timestamp} with time
* @returns {Timestamp[]} The requested array of interval {@link Timestamp}s
*/
function createIntervalList (timestamp, first, minutes, count, now) {
const intervals = [];
for (let i = 0; i < count; ++i) {
const mins = (first + i) * minutes;
const ts = copyTimestamp(timestamp);
intervals.push(updateMinutes(ts, mins, now));
}
return intervals
}
/**
* @callback getOptions
* @param {Timestamp} timestamp A {@link Timestamp} object
* @param {boolean} short True if using short options
* @returns {Object} An Intl object representing optioons to be used
*/
/**
* @callback formatter
* @param {Timestamp} timestamp The {@link Timestamp} being used
* @param {boolean} short If short format is being requested
* @returns {string} The localized string of the formatted {@link Timestamp}
*/
/**
* Returns a function that uses Intl.DateTimeFormat formatting
* @param {string} locale The locale to use (ie: en-US)
* @param {getOptions} cb The function to call for options. This function should return an Intl formatted object. The function is passed (timestamp, short).
* @returns {formatter} The function has params (timestamp, short). The short is to use the short options.
*/
function createNativeLocaleFormatter (locale, cb) {
const emptyFormatter = (_t, _s) => '';
/* istanbul ignore next */
if (typeof Intl === 'undefined' || typeof Intl.DateTimeFormat === 'undefined') {
return emptyFormatter
}
return (timestamp, short) => {
try {
const intlFormatter = new Intl.DateTimeFormat(locale || undefined, cb(timestamp, short));
return intlFormatter.format(makeDateTime(timestamp))
}
catch (e) /* istanbul ignore next */ {
/* eslint-disable-next-line */
console.error(`Intl.DateTimeFormat: ${e.message} -> ${getDateTime(timestamp)}`);
return emptyFormatter
}
}
}
/**
* Makes a JavaScript Date from the passed {@link Timestamp}
* @param {Timestamp} timestamp The {@link Timestamp} to use
* @param {boolean} utc True to get Date object using UTC
* @returns {date} A JavaScript Date
*/
function makeDate (timestamp, utc = true) {
if (utc) return new Date(Date.UTC(timestamp.year, timestamp.month - 1, timestamp.day, 0, 0))
return new Date(timestamp.year, timestamp.month - 1, timestamp.day, 0, 0)
}
/**
* Makes a JavaScript Date from the passed {@link Timestamp} (with time)
* @param {Timestamp} timestamp The {@link Timestamp} to use
* @param {boolean} utc True to get Date object using UTC
* @returns {date} A JavaScript Date
*/
function makeDateTime (timestamp, utc = true) {
if (utc) return new Date(Date.UTC(timestamp.year, timestamp.month - 1, timestamp.day, timestamp.hour, timestamp.minute))
return new Date(timestamp.year, timestamp.month - 1, timestamp.day, timestamp.hour, timestamp.minute)
}
// validate a number IS a number
/**
* Teting is passed value is a number
* @param {(string|number)} input The value to use
* @returns {boolean} True if a number (not floating point)
*/
function validateNumber (input) {
return isFinite(parseInt(input, 10))
}
/**
* Given an array of {@link Timestamp}s, finds the max date (and possible time)
* @param {Timestamp[]} timestamps This is an array of {@link Timestamp}s
* @param {boolean=} useTime Default false; if true, uses time in the comparison as well
* @returns The {@link Timestamp} with the highest date (and possibly time) value
*/
function maxTimestamp (timestamps, useTime = false) {
const func = useTime === true ? getDayTimeIdentifier : getDayIdentifier;
return timestamps.reduce((prev, cur) => {
return Math.max(func(prev), func(cur)) === func(prev) ? prev : cur
})
}
/**
* Given an array of {@link Timestamp}s, finds the min date (and possible time)
* @param {Timestamp[]} timestamps This is an array of {@link Timestamp}s
* @param {boolean=} useTime Default false; if true, uses time in the comparison as well
* @returns The {@link Timestamp} with the lowest date (and possibly time) value
*/
function minTimestamp (timestamps, useTime = false) {
const func = useTime === true ? getDayTimeIdentifier : getDayIdentifier;
return timestamps.reduce((prev, cur) => {
return Math.min(func(prev), func(cur)) === func(prev) ? prev : cur
})
}
/**
* Determines if the passed {@link Timestamp} is between (or equal) to two {@link Timestamp}s (range)
* @param {Timestamp} timestamp The {@link Timestamp} for testing
* @param {Timestamp} startTimestamp The starting {@link Timestamp}
* @param {Timestamp} endTimestamp The ending {@link Timestamp}
* @param {boolean=} useTime If true, use time from the {@link Timestamp}s
* @returns {boolean} True if {@link Timestamp} is between (or equal) to two {@link Timestamp}s (range)
*/
function isBetweenDates (timestamp, startTimestamp, endTimestamp, useTime /* = false */) {
const cd = getDayIdentifier(timestamp) + (useTime === true ? getTimeIdentifier(timestamp) : 0);
const sd = getDayIdentifier(startTimestamp) + (useTime === true ? getTimeIdentifier(startTimestamp) : 0);
const ed = getDayIdentifier(endTimestamp) + (useTime === true ? getTimeIdentifier(endTimestamp) : 0);
return cd >= sd && cd <= ed
}
/**
* Determine if two ranges of {@link Timestamp}s overlap each other
* @param {Timestamp} startTimestamp The starting {@link Timestamp} of first range
* @param {Timestamp} endTimestamp The endinging {@link Timestamp} of first range
* @param {Timestamp} firstTimestamp The starting {@link Timestamp} of second range
* @param {Timestamp} lastTimestamp The ending {@link Timestamp} of second range
* @returns {boolean} True if the two ranges overlap each other
*/
function isOverlappingDates (startTimestamp, endTimestamp, firstTimestamp, lastTimestamp) {
const start = getDayIdentifier(startTimestamp);
const end = getDayIdentifier(endTimestamp);
const first = getDayIdentifier(firstTimestamp);
const last = getDayIdentifier(lastTimestamp);
return (
(start >= first && start <= last) // overlap left
|| (end >= first && end <= last) // overlap right
|| (first >= start && end >= last) // surrounding
)
}
/**
* Add or decrements years, months, days, hours or minutes to a timestamp
* @param {Timestamp} timestamp The {@link Timestamp} object
* @param {Object} options configuration data
* @param {number=} options.year If positive, adds years. If negative, removes years.
* @param {number=} options.month If positive, adds months. If negative, removes month.
* @param {number=} options.day If positive, adds days. If negative, removes days.
* @param {number=} options.hour If positive, adds hours. If negative, removes hours.
* @param {number=} options.minute If positive, adds minutes. If negative, removes minutes.
* @returns {Timestamp} A modified copy of the passed in {@link Timestamp}
*/
function addToDate (timestamp, options) {
const ts = copyTimestamp(timestamp);
let minType;
__forEachObject(options, (value, key) => {
if (ts[ key ] !== undefined) {
ts[ key ] += parseInt(value, 10);
const indexType = NORMALIZE_TYPES.indexOf(key);
if (indexType !== -1) {
if (minType === undefined) {
minType = indexType;
}
else {
/* istanbul ignore next */
minType = Math.min(indexType, minType);
}
}
}
});
// normalize timestamp
if (minType !== undefined) {
__normalize(ts, NORMALIZE_TYPES[ minType ]);
}
updateFormatted(ts);
return ts
}
const NORMALIZE_TYPES = [ 'minute', 'hour', 'day', 'month' ];
// addToDate helper
function __forEachObject (obj, cb) {
Object.keys(obj).forEach(k => cb(obj[ k ], k));
}
// normalize minutes
function __normalizeMinute (ts) {
if (ts.minute >= MINUTES_IN_HOUR || ts.minute < 0) {
const hours = Math.floor(ts.minute / MINUTES_IN_HOUR);
ts.minute -= hours * MINUTES_IN_HOUR;
ts.hour += hours;
__normalizeHour(ts);
}
return ts
}
// normalize hours
function __normalizeHour (ts) {
if (ts.hour >= HOURS_IN_DAY || ts.hour < 0) {
const days = Math.floor(ts.hour / HOURS_IN_DAY);
ts.hour -= days * HOURS_IN_DAY;
ts.day += days;
__normalizeDay(ts);
}
return ts
}
// normalize days
function __normalizeDay (ts) {
__normalizeMonth(ts);
let dim = daysInMonth(ts.year, ts.month);
if (ts.day > dim) {
++ts.month;
if (ts.month > MONTH_MAX) {
__normalizeMonth(ts);
}
let days = ts.day - dim;
dim = daysInMonth(ts.year, ts.month);
do {
if (days > dim) {
++ts.month;
if (ts.month > MONTH_MAX) {
__normalizeMonth(ts);
}
days -= dim;
dim = daysInMonth(ts.year, ts.month);
}
} while (days > dim)
ts.day = days;
}
else if (ts.day <= 0) {
let days = -1 * ts.day;
--ts.month;
if (ts.month <= 0) {
__normalizeMonth(ts);
}
dim = daysInMonth(ts.year, ts.month);
do {
if (days > dim) /* istanbul ignore next */ {
days -= dim;
--ts.month;
if (ts.month <= 0) {
__normalizeMonth(ts);
}
dim = daysInMonth(ts.year, ts.month);
}
} while (days > dim)
ts.day = dim - days;
}
return ts
}
// normalize months
function __normalizeMonth (ts) {
if (ts.month > MONTH_MAX) {
const years = Math.floor(ts.month / MONTH_MAX);
ts.month = ts.month % MONTH_MAX;
ts.year += years;
}
else if (ts.month < MONTH_MIN) {
ts.month += MONTH_MAX;
--ts.year;
}
return ts
}
// normalize all
function __normalize (ts, type) {
switch (type) {
case 'minute':
return __normalizeMinute(ts)
case 'hour':
return __normalizeHour(ts)
case 'day':
return __normalizeDay(ts)
case 'month':
return __normalizeMonth(ts)
}
}
/**
* Returns number of days between two {@link Timestamp}s
* @param {Timestamp} ts1 The first {@link Timestamp}
* @param {Timestamp} ts2 The second {@link Timestamp}
* @returns Number of days
*/
function daysBetween (ts1, ts2) {
const diff = diffTimestamp(ts1, ts2, true);
return Math.floor(diff / MILLISECONDS_IN_DAY)
}
/**
* Returns number of weeks between two {@link Timestamp}s
* @param {Timestamp} ts1 The first {@link Timestamp}
* @param {Timestamp} ts2 The second {@link Timestamp}
*/
function weeksBetween (ts1, ts2) {
let t1 = copyTimestamp(ts1);
let t2 = copyTimestamp(ts2);
t1 = findWeekday(t1, 0);
t2 = findWeekday(t2, 6);
return Math.ceil(daysBetween(t1, t2) / DAYS_IN_WEEK)
}
// Known dates - starting week on a monday to conform to browser
const weekdayDateMap = {
Sun: new Date('2020-01-05T00:00:00.000Z'),
Mon: new Date('2020-01-06T00:00:00.000Z'),
Tue: new Date('2020-01-07T00:00:00.000Z'),
Wed: new Date('2020-01-08T00:00:00.000Z'),
Thu: new Date('2020-01-09T00:00:00.000Z'),
Fri: new Date('2020-01-10T00:00:00.000Z'),
Sat: new Date('2020-01-11T00:00:00.000Z')
};
function getWeekdayFormatter () {
const emptyFormatter = (_d, _t) => '';
const options = {
long: { timeZone: 'UTC', weekday: 'long' },
short: { timeZone: 'UTC', weekday: 'short' },
narrow: { timeZone: 'UTC', weekday: 'narrow' }
};
/* istanbul ignore next */
if (typeof Intl === 'undefined' || typeof Intl.DateTimeFormat === 'undefined') {
return emptyFormatter
}
// type = 'narrow', 'short', 'long'
function weekdayFormatter (weekday, type, locale) {
try {
const intlFormatter = new Intl.DateTimeFormat(locale || undefined, options[ type ] || options[ 'long' ]);
return intlFormatter.format(weekdayDateMap[ weekday ])
}
catch (e) /* istanbul ignore next */ {
/* eslint-disable-next-line */
console.error(`Intl.DateTimeFormat: ${e.message} -> day of week: ${ weekday }`);
return emptyFormatter
}
}
return weekdayFormatter
}
function getWeekdayNames (type, locale) {
const shortWeekdays = Object.keys(weekdayDateMap);
const weekdayFormatter = getWeekdayFormatter();
return shortWeekdays.map(weekday => weekdayFormatter(weekday, type, locale))
}
function getMonthFormatter () {
const emptyFormatter = (_m, _t) => '';
const options = {
long: { timeZone: 'UTC', month: 'long' },
short: { timeZone: 'UTC', month: 'short' },
narrow: { timeZone: 'UTC', month: 'narrow' }
};
/* istanbul ignore next */
if (typeof Intl === 'undefined' || typeof Intl.DateTimeFormat === 'undefined') {
return emptyFormatter
}
// type = 'narrow', 'short', 'long'
function monthFormatter (month, type, locale) {
try {
const intlFormatter = new Intl.DateTimeFormat(locale || undefined, options[ type ] || options[ 'long' ]);
const date = new Date();
date.setDate(1);
date.setMonth(month);
return intlFormatter.format(date)
}
catch (e) /* istanbul ignore next */ {
/* eslint-disable-next-line */
console.error(`Intl.DateTimeFormat: ${e.message} -> month: ${ month }`);
return emptyFormatter
}
}
return monthFormatter
}
function getMonthNames (type, locale) {
const monthFormatter = getMonthFormatter();
return [...Array(12).keys()]
.map(month => monthFormatter(month, type, locale))
}
function convertToUnit (input, unit = 'px') {
if (input == null || input === '') {
return undefined
}
else if (isNaN(input)) {
return String(input)
}
else if (input === 'auto') {
return input
}
else {
return `${ Number(input) }${ unit }`
}
}
function indexOf (array, cb) {
for (let i = 0; i < array.length; i++) {
if (cb(array[ i ], i) === true) {
return i
}
}
return -1
}
function minCharWidth (str, count) {
if (count === 0) return str
return str.slice(0, count)
}
const toCamelCase = str => str.replace(/(-\w)/g, m => m[ 1 ].toUpperCase());
let $listeners, $emit;
/**
* Used by render function to set up specialized mouse events
* The mouse event will not be set if there is no listener for it to key callbacks to a minimum
* @param {Object} events undecorated events (ie: 'click-day' will be transformed to 'onClickDay')
* @param {Function} getEvent callback for event
* @returns {Object} contents of decorated mouse events
*/
function getMouseEventHandlers (events, getEvent) {
const on = {};
for (const eventName in events) {
const eventOptions = events[ eventName ];
// convert eventName to vue camelCase (decorated)
const eventKey = toCamelCase('on-' + eventName);
// make sure listeners has been set up properly
if ($listeners === undefined) {
// someone forgot to call the default function export
console.warn('$listeners has not been set up');
return
}
// if there is no listener for this, then don't process it
if ($listeners.value[ eventKey ] === undefined) continue
// https://vuejs.org/v2/guide/render-function.html#Event-amp-Key-Modifiers
// const prefix = eventOptions.passive ? '&' : ((eventOptions.once ? '~' : '') + (eventOptions.capture ? '!' : ''))
// const key = prefix + eventOptions.event
// prefix 'on' and capitalize first character
const key = 'on' + eventOptions.event.charAt(0).toUpperCase() + eventOptions.event.slice(1);
const handler = (event) => {
const mouseEvent = event;
if (eventOptions.button === undefined || (mouseEvent.buttons > 0 && mouseEvent.button === eventOptions.button)) {
if (eventOptions.prevent) {
mouseEvent.preventDefault();
}
if (eventOptions.stop) {
mouseEvent.stopPropagation();
}
$emit(eventName, getEvent(mouseEvent, eventName));
}
return eventOptions.result
};
if (key in on) {
if (Array.isArray(on[ key ])) {
(on[ key ]).push(handler);
}
else {
on[ key ] = [ on[ key ], handler ];
}
}
else {
on[ key ] = handler;
}
}
return on
}
/**
*
* @param {String} suffix
* @param {Function} getEvent The callback
* @returns {Object} contains decorated mouse events based on listeners of that event and for each a callback
*/
function getDefaultMouseEventHandlers (suffix, getEvent) {
return getMouseEventHandlers(getMouseEventName(suffix), getEvent)
}
/**
*
* @param {String} suffix
* @returns {Object}
*/
function getMouseEventName (suffix) {
return {
[ 'click' + suffix ]: { event: 'click' },
[ 'contextmenu' + suffix ]: { event: 'contextmenu', prevent: true, result: false },
[ 'mousedown' + suffix ]: { event: 'mousedown' },
[ 'mousemove' + suffix ]: { event: 'mousemove' },
[ 'mouseup' + suffix ]: { event: 'mouseup' },
[ 'mouseenter' + suffix ]: { event: 'mouseenter' },
[ 'mouseleave' + suffix ]: { event: 'mouseleave' },
[ 'touchstart' + suffix ]: { event: 'touchstart' },
[ 'touchmove' + suffix ]: { event: 'touchmove' },
[ 'touchend' + suffix ]: { event: 'touchend' }
}
}
/**
*
* @param {String} suffix
* @returns {Array} the array or raw listeners (ie: 'click-day' as opposed to 'onClickDay')
*/
function getRawMouseEvents (suffix) {
return Object.keys(getMouseEventName(suffix))
}
/**
* export of default funtion
* @param {VTTCue.emit} emit
* @param {Array} listeners on the instance
*/
function useMouse (emit, listeners) {
$emit = emit;
$listeners = listeners;
return {
getMouseEventHandlers,
getDefaultMouseEventHandlers,
getMouseEventName,
getRawMouseEvents
}
}
var ResizeObserver$1 = {
name: 'ResizeObserver',
mounted (el, { modifiers, value }) {
if (!value) return // callback
const opts = {};
opts.callback = value;
opts.size = { width: 0, height: 0 };
opts.observer = new ResizeObserver(entries => {
const rect = entries[ 0 ].contentRect;
if (rect.width !== opts.size.width || rect.height !== opts.size.height) {
opts.size.width = rect.width;
opts.size.height = rect.height;
opts.callback(opts.size);
}
});
// start the observing
opts.observer.observe(el);
// save to element
el.__onResizeObserver = opts;
},
beforeUnmount (el) {
if (!el.__onResizeObserver) return
const { observer } = el.__onResizeObserver;
observer.unobserve(el);
delete el.__onResizeObserver;
}
};
/**
* The main QCalendar wrapper
* All others are a child to this one
*/
function useCalendar (props, renderFunc, { scrollArea, pane }) {
if (!renderFunc) {
const msg = '[error: renderCalendar] no renderFunc has been supplied to useCalendar';
console.error(msg);
throw new Error(msg)
}
const size = reactive({ width: 0, height: 0 }),
rootRef = ref(null);
function __onResize({ width, height }) {
size.width = width;
size.height = height;
}
const scrollWidth = computed(() => {
return props.noScroll !== true
? scrollArea.value && pane.value && size.height // force recalc with height change
? scrollArea.value.offsetWidth - pane.value.offsetWidth
: 0
: 0
});
function __initCalendar() {
//
}
function __renderCalendar() {
const data = {
ref: rootRef,
role: 'complementary',
lang: props.locale,
class: `q-calendar ${props.dark ? 'q-calendar--dark' : ''} ${props.bordered ? 'q-calendar__bordered' : ''}`,
};
return withDirectives(h('div', { ...data }, [renderFunc()]), [[ResizeObserver$1, __onResize]])
}
return {
rootRef,
scrollWidth,
__initCalendar,
__renderCalendar,
}
}
// common composables
({
modelValue: { // v-model
type: String,
default: today(),
validator: v => v === '' || validateTimestamp(v)
},
weekdays: {
type: Array,
default: () => [ 0, 1, 2, 3, 4, 5, 6 ]
},
dateType: {
type: String,
default: 'round',
validator: v => [ 'round', 'rounded', 'square' ].includes(v)
},
weekdayAlign: {
type: String,
default: 'center',
validator: v => [ 'left', 'center', 'right' ].includes(v)
},
dateAlign: