diff --git a/api/js/etemplate/Et2Date/Et2Date.ts b/api/js/etemplate/Et2Date/Et2Date.ts index f79f7f341c..35e76c5e30 100644 --- a/api/js/etemplate/Et2Date/Et2Date.ts +++ b/api/js/etemplate/Et2Date/Et2Date.ts @@ -16,6 +16,10 @@ import {Et2InputWidget} from "../Et2InputWidget/Et2InputWidget"; import {dateStyles} from "./DateStyles"; import {LitFlatpickr} from "lit-flatpickr"; import "flatpickr/dist/plugins/scrollPlugin.js"; +import {holidays} from "./Holidays"; + +// Request this year's holidays now +holidays(new Date().getFullYear()); // list of existing localizations from node_modules/flatpicker/dist/l10n directory: const l10n = [ @@ -333,6 +337,8 @@ export class Et2Date extends Et2InputWidget(FormControlMixin(ValidateMixin(LitFl constructor() { super(); + + this._onDayCreate = this._onDayCreate.bind(this); } @@ -386,6 +392,8 @@ export class Et2Date extends Et2InputWidget(FormControlMixin(ValidateMixin(LitFl options.dateFormat = "Y-m-dT00:00:00\\Z"; options.weekNumbers = true; + options.onDayCreate = this._onDayCreate; + this._localize(options); if(this.inline) @@ -490,6 +498,63 @@ export class Et2Date extends Et2InputWidget(FormControlMixin(ValidateMixin(LitFl this.modelValue = this.getValue(); } + /** + * Customise date rendering + * + * @see https://flatpickr.js.org/events/ + * + * @param {Date} dates Currently selected date(s) + * @param dStr a string representation of the latest selected Date object by the user. The string is formatted as per the dateFormat option. + * @param inst flatpickr instance + * @param dayElement + * @protected + */ + protected _onDayCreate(dates : Date[], dStr : string, inst, dayElement : HTMLElement) + { + //@ts-ignore flatpickr adds dateObj to days + let date = new Date(dayElement.dateObj); + let f_date = new Date(date.valueOf() - date.getTimezoneOffset() * 60 * 1000); + if(!f_date) + { + return; + } + + let set_holiday = function(holidays, element) + { + let day_holidays = holidays[formatDate(f_date, {dateFormat: "Ymd"})] + let tooltip = ''; + if(typeof day_holidays !== 'undefined' && day_holidays.length) + { + for(var i = 0; i < day_holidays.length; i++) + { + if(typeof day_holidays[i]['birthyear'] !== 'undefined') + { + element.classList.add('calBirthday'); + } + else + { + element.classList.add('calHoliday'); + } + tooltip += day_holidays[i]['name'] + "\n"; + } + } + if(tooltip) + { + this.egw().tooltipBind(element, tooltip); + } + }.bind(this); + + let holiday_list = holidays(f_date.getFullYear()); + if(holiday_list instanceof Promise) + { + holiday_list.then((h) => {set_holiday(h, dayElement);}); + } + else + { + set_holiday(holiday_list, dayElement); + } + } + /** * Set the minimum allowed date * @param {string | Date} min diff --git a/api/js/etemplate/Et2Date/Holidays.ts b/api/js/etemplate/Et2Date/Holidays.ts new file mode 100644 index 0000000000..935ef4da83 --- /dev/null +++ b/api/js/etemplate/Et2Date/Holidays.ts @@ -0,0 +1,40 @@ +/** + * Static holiday cache + * access through holidays(year) + */ +let _holiday_cache = {}; + +/** + * Get a list of holidays for the given year + * + * Returns either a list of holidays indexed by date, in Ymd format: + * {20001225: {day: 14, month: 2, occurence: 2021, name: "Valentinstag"}} + * or a promise that resolves with the list. + * + * No need to cache the results, we do it here. + * + * @param year + * @returns Promise | Object + */ +export function holidays(year) : Promise | {} +{ + // No country selected causes error, so skip if it's missing + if(!egw.preference('country', 'common')) + { + return {}; + } + + if(typeof _holiday_cache[year] === 'undefined') + { + // Fetch with json instead of jsonq because there may be more than + // one widget listening for the response by the time it gets back, + // and we can't do that when it's queued. + _holiday_cache[year] = window.fetch( + egw.link('/calendar/holidays.php', {year: year}) + ).then((response) => + { + return _holiday_cache[year] = response.json(); + }); + } + return _holiday_cache[year]; +} diff --git a/api/templates/default/etemplate2.css b/api/templates/default/etemplate2.css index 18e5527870..b42ae904b7 100644 --- a/api/templates/default/etemplate2.css +++ b/api/templates/default/etemplate2.css @@ -4017,6 +4017,15 @@ body .flatpickr-calendar { display: none; } +.flatpickr-calendar.open { + z-index: 1000; +} + +/** Holidays & birthdays **/ +div.flatpickr-calendar .calHoliday, div.flatpickr-calendar .calBirthday { + background-color: rgba(103, 159, 210, 0.5); +} + /** Sizing for inline flatpickr, used in calendar sidebox **/ div.flatpickr-calendar.inline { --dayWidth: calc((width - 80) / 7); diff --git a/calendar/js/SidemenuDate.ts b/calendar/js/SidemenuDate.ts index 5915eeacc8..d114247a23 100644 --- a/calendar/js/SidemenuDate.ts +++ b/calendar/js/SidemenuDate.ts @@ -1,6 +1,5 @@ -import {Et2Date, formatDate, parseDate} from "../../api/js/etemplate/Et2Date/Et2Date"; +import {Et2Date, parseDate} from "../../api/js/etemplate/Et2Date/Et2Date"; import {css} from "@lion/core"; -import {et2_calendar_view} from "./et2_widget_view"; import {CalendarApp} from "./app"; export class SidemenuDate extends Et2Date @@ -36,8 +35,6 @@ export class SidemenuDate extends Et2Date ]; } - static _holidays : Object = null; - constructor() { super(); @@ -54,30 +51,6 @@ export class SidemenuDate extends Et2Date this.removeEventListener("change", this._oldChange); this.addEventListener("change", this._handleChange); - if(null == SidemenuDate._holidays) - { - let holidays_or_promise = et2_calendar_view.get_holidays((new Date).getFullYear()); - if(holidays_or_promise instanceof Promise) - { - holidays_or_promise.then(holidays => - { - SidemenuDate._holidays = holidays; - if(this._instance) - { - // Already drawn without holidays so redraw - this._instance.redraw(); - } - else - { - this.requestUpdate(); - } - }) - } - else - { - SidemenuDate._holidays = holidays_or_promise; - } - } } disconnectedCallback() @@ -140,7 +113,6 @@ export class SidemenuDate extends Et2Date options.dateFormat = "Y-m-dT00:00:00\\Z"; options.shorthandCurrentMonth = true; - options.onDayCreate = this._onDayCreate; options.onMonthChange = this._updateGoButton; options.nextArrow = ""; @@ -161,49 +133,6 @@ export class SidemenuDate extends Et2Date } } - /** - * Customise date rendering - * - * @see https://flatpickr.js.org/events/ - * - * @param {Date} dates Currently selected date(s) - * @param dStr a string representation of the latest selected Date object by the user. The string is formatted as per the dateFormat option. - * @param inst flatpickr instance - * @param dayElement - * @protected - */ - protected _onDayCreate(dates : Date[], dStr : string, inst, dayElement : HTMLElement) - { - //@ts-ignore flatpickr adds dateObj to days - let date = new Date(dayElement.dateObj); - let f_date = new Date(date.valueOf() - date.getTimezoneOffset() * 60 * 1000); - if(!f_date || SidemenuDate._holidays === null) - { - return; - } - - let day_holidays = SidemenuDate._holidays[formatDate(f_date, {dateFormat: "Ymd"})] - var tooltip = ''; - if(typeof day_holidays !== 'undefined' && day_holidays.length) - { - for(var i = 0; i < day_holidays.length; i++) - { - if(typeof day_holidays[i]['birthyear'] !== 'undefined') - { - dayElement.classList.add('calendar_calBirthday'); - } - else - { - dayElement.classList.add('calendar_calHoliday'); - } - tooltip += day_holidays[i]['name'] + "\n"; - } - } - if(tooltip) - { - this.egw().tooltipBind(dayElement, tooltip); - } - } /** * Handler for change events. Re-bound to be able to cancel month changes, since it's an input and emits them