diff --git a/api/js/etemplate/Et2Date/Et2Date.ts b/api/js/etemplate/Et2Date/Et2Date.ts index 3c509406cf..955e342af0 100644 --- a/api/js/etemplate/Et2Date/Et2Date.ts +++ b/api/js/etemplate/Et2Date/Et2Date.ts @@ -518,7 +518,7 @@ export class Et2Date extends Et2InputWidget(FormControlMixin(LitFlatpickr)) // Remove Lion's inert attribute so we can work in Et2Dialog options.onOpen = [() => { - this._instance.calendarContainer.removeAttribute("inert") + this._instance.calendarContainer?.removeAttribute("inert") }]; return options; @@ -682,7 +682,7 @@ export class Et2Date extends Et2InputWidget(FormControlMixin(LitFlatpickr)) let parsedDate = null try { - parsedDate = this._instance.parseDate(value, this._instance.config.altFormat) + parsedDate = this._instance.parseDate(value, this.getOptions().altFormat) } catch(e) { @@ -691,10 +691,10 @@ export class Et2Date extends Et2InputWidget(FormControlMixin(LitFlatpickr)) // If they typed a valid date/time, try to update flatpickr if(parsedDate) { - const formattedDate = this._instance.formatDate(parsedDate, this._instance.config.altFormat) + const formattedDate = this._instance.formatDate(parsedDate, this.getOptions().altFormat) if(value === formattedDate && // Avoid infinite loop of setting the same value back triggering another change - this._instance.input.value !== this._instance.formatDate(parsedDate, this._instance.config.dateFormat)) + this._instance.input.value !== this._instance.formatDate(parsedDate, this.getOptions().dateFormat)) { this._instance.setDate(value, true, this._instance.config.altFormat) } diff --git a/api/js/etemplate/Et2Date/Et2DateRange.ts b/api/js/etemplate/Et2Date/Et2DateRange.ts new file mode 100644 index 0000000000..84659c8c00 --- /dev/null +++ b/api/js/etemplate/Et2Date/Et2DateRange.ts @@ -0,0 +1,331 @@ +import {Et2InputWidget} from "../Et2InputWidget/Et2InputWidget"; +import {FormControlMixin} from "@lion/form-core"; +import {classMap, css, html, ifDefined, LitElement, render, TemplateResult} from "@lion/core"; +import shoelace from "../Styles/shoelace"; +import {dateStyles} from "./DateStyles"; +import flatpickr from "flatpickr"; +import "flatpickr/dist/plugins/rangePlugin"; +import {Et2Date, formatDate, parseDate} from "./Et2Date"; +import {egw} from "../../jsapi/egw_global"; + +/** + * Display a time duration (eg: 3 days, 6 hours) + * + * If not specified, the time is in assumed to be minutes and will be displayed with a calculated unit + * but this can be specified with the properties. + */ +export class Et2DateRange extends Et2InputWidget(FormControlMixin(LitElement)) +{ + static get styles() + { + return [ + ...super.styles, + shoelace, + ...dateStyles, + css` + + .input-group { + display: flex; + flex-direction: row; + flex-wrap: nowrap; + align-items: baseline; + } + `, + ]; + } + + static get properties() + { + return { + ...super.properties, + /** + * Is the date range relative (this week) or absolute (2016-02-15 - 2016-02-21). This will affect the value returned. + */ + relative: {type: Boolean}, + + /** + * An object with keys 'from' and 'to' for absolute ranges, or a relative range string + */ + value: {type: Object} + } + } + + constructor() + { + super(); + } + + getUpdateComplete() { + const p = super.getUpdateComplete(); + if(!this.relative) + { + p.then(() => this.setupFlatpickr()); + } + return p; + } + protected setupFlatpickr() + { + if(!this.fromElement || !this.fromElement._inputElement) return; + + this.fromElement._instance = flatpickr((this.fromElement).findInputField(), { + ...(this.fromElement).getOptions(), + ...{ + plugins: [ + // @ts-ignore ts can't find rangePlugin in IDE + rangePlugin({ + input: this.toElement + }) + ] + } + }); + } + + render() + { + const hasLabel = this.label ? true : false + const hasHelpText = this.helpText ? true : false; + + return html` +
+
+ +
+
${this._inputGroupTemplate()}
+ + ${this.helpText} + +
+ `; + } + + protected _inputGroupTemplate() : TemplateResult + { + return html` + + ${this.relative ? this._inputRelativeTemplate() : this._inputAbsoluteTemplate()} + + `; + } + + /** + * We're doing a relative date range, show the relative options + * @returns {TemplateResult} + * @protected + */ + protected _inputRelativeTemplate() : TemplateResult + { + return html``; + } + + /** + * We're doing an absolute date range, we need start and end dates + * + * @returns {TemplateResult} + * @protected + */ + protected _inputAbsoluteTemplate() : TemplateResult + { + return html` + + + `; + } + + public get fromElement() : HTMLElement + { + return this.shadowRoot?.querySelector("[name='from']"); + } + public get toElement() : HTMLElement + { + return this.shadowRoot?.querySelector("[name='to']"); + } + public get relativeElement() : HTMLElement + { + return this.shadowRoot?.querySelector("[name='relative']"); + } + + public get value() : {to:string,from:string}|string + { + if(this.relative) + { + return this.relativeElement?.value || ""; + } + let val = { + from: this.fromElement?.findInputField()?.value || null, + to: this.toElement?.value || null + } + if(val.from) val.from = formatDate(parseDate(val.from), {dateFormat:"Y-m-dT00:00:00Z"}); + if(val.to) val.to = formatDate(parseDate(val.to), {dateFormat:"Y-m-dT00:00:00Z"}); + return (val.from || val.to) ? val : null; + } + + public set value(new_value : {to:string,from:string}|string) + { + if(this.relative) + { + this.relativeElement.value = new_value; + } + else if (this.fromElement && this.toElement) + { + this.fromElement._instance.setDate( [new_value?.from, new_value?.to],true); + } + } + + // Class Constants + static readonly relative_dates = [ + // Start and end are relative offsets, see et2_date.set_min() + // or Date objects + { + value: 'Today', + label: egw.lang('Today'), + from(date) {return date;}, + to(date) {return date;} + }, + { + label: egw.lang('Yesterday'), + value: 'Yesterday', + from(date) { + date.setUTCDate(date.getUTCDate() - 1); + return date; + }, + to: '' + }, + { + label: egw.lang('This week'), + value: 'This week', + from(date) {return egw.week_start(date);}, + to(date) { + date.setUTCDate(date.getUTCDate() + 6); + return date; + } + }, + { + label: egw.lang('Last week'), + value: 'Last week', + from(date) { + var d = egw.week_start(date); + d.setUTCDate(d.getUTCDate() - 7); + return d; + }, + to(date) { + date.setUTCDate(date.getUTCDate() + 6); + return date; + } + }, + { + label: egw.lang('This month'), + value: 'This month', + from(date) + { + date.setUTCDate(1); + return date; + }, + to(date) + { + date.setUTCMonth(date.getUTCMonth()+1); + date.setUTCDate(0); + return date; + } + }, + { + label: egw.lang('Last month'), + value: 'Last month', + from(date) + { + date.setUTCMonth(date.getUTCMonth() - 1); + date.setUTCDate(1); + return date; + }, + to(date) + { + date.setUTCMonth(date.getUTCMonth()+1); + date.setUTCDate(0); + return date; + } + }, + { + label: egw.lang('Last 3 months'), + value: 'Last 3 months', + from(date) + { + date.setUTCMonth(date.getUTCMonth() - 2); + date.setUTCDate(1); + return date; + }, + to(date) + { + date.setUTCMonth(date.getUTCMonth()+3); + date.setUTCDate(0); + return date; + } + }, + { + label: egw.lang('This year'), + value: 'This year', + from(d) { + d.setUTCMonth(0); + d.setUTCDate(1); + return d; + }, + to(d) { + d.setUTCMonth(11); + d.setUTCDate(31); + return d; + } + }, + { + label: egw.lang('Last year'), + value: 'Last year', + from(d) { + d.setUTCMonth(0); + d.setUTCDate(1); + d.setUTCYear(d.getUTCYear() - 1); + return d; + }, + to(d) { + d.setUTCMonth(11); + d.setUTCDate(31); + d.setUTCYear(d.getUTCYear() - 1); + return d; + } + } + ]; +} + +customElements.define("et2-date-range", Et2DateRange); \ No newline at end of file diff --git a/api/js/etemplate/etemplate2.ts b/api/js/etemplate/etemplate2.ts index 9f12a82246..504d280e8a 100644 --- a/api/js/etemplate/etemplate2.ts +++ b/api/js/etemplate/etemplate2.ts @@ -37,6 +37,7 @@ import './Et2Checkbox/Et2CheckboxReadonly'; import './Et2Date/Et2Date'; import './Et2Date/Et2DateDuration'; import './Et2Date/Et2DateDurationReadonly'; +import './Et2Date/Et2DateRange'; import './Et2Date/Et2DateReadonly'; import './Et2Date/Et2DateSinceReadonly'; import './Et2Date/Et2DateTime';