mirror of
https://github.com/EGroupware/egroupware.git
synced 2024-11-07 16:44:07 +01:00
Calendar: Fix some kanban listen problems
- Status filter isn't really applicable - Priority was missing its options
This commit is contained in:
parent
924bea48e5
commit
59e64cd908
@ -390,7 +390,7 @@ export class Et2Date extends Et2InputWidget(FormControlMixin(ValidateMixin(LitFl
|
||||
* @see https://flatpickr.js.org/options/
|
||||
* @returns {any}
|
||||
*/
|
||||
protected getOptions()
|
||||
getOptions()
|
||||
{
|
||||
let options = super.getOptions();
|
||||
|
||||
|
356
api/js/etemplate/Et2Date/Et2DateRange.ts
Normal file
356
api/js/etemplate/Et2Date/Et2DateRange.ts
Normal file
@ -0,0 +1,356 @@
|
||||
import {Et2InputWidget} from "../Et2InputWidget/Et2InputWidget";
|
||||
import {dateStyles} from "./DateStyles";
|
||||
import {css, html, LitElement, repeat, TemplateResult} from "@lion/core";
|
||||
import {egw} from "../../jsapi/egw_global";
|
||||
import {Et2Select} from "../Et2Select/Et2Select";
|
||||
import {Et2widgetWithSelectMixin} from "../Et2Select/Et2WidgetWithSelectMixin";
|
||||
import {SelectOption} from "../Et2Select/FindSelectOptions";
|
||||
import "flatpickr/dist/plugins/rangePlugin.js";
|
||||
import {Et2Date} from "./Et2Date";
|
||||
|
||||
export class Et2DateRange extends Et2widgetWithSelectMixin(Et2InputWidget(LitElement))
|
||||
{
|
||||
static get styles()
|
||||
{
|
||||
return [
|
||||
...super.styles,
|
||||
dateStyles,
|
||||
css`
|
||||
:host {
|
||||
width: auto;
|
||||
}
|
||||
`,
|
||||
];
|
||||
}
|
||||
|
||||
static get properties()
|
||||
{
|
||||
return {
|
||||
...super.properties,
|
||||
/**
|
||||
* An object with keys 'from' and 'to' for absolute ranges, or a relative range string
|
||||
*/
|
||||
value: {type: Object},
|
||||
/**
|
||||
* Is the date range relative (this week) or absolute (2016-02-15 - 2016-02-21). This will affect the value returned.
|
||||
*/
|
||||
relative: {type: Boolean},
|
||||
|
||||
placeholder: {type: String}
|
||||
}
|
||||
}
|
||||
|
||||
// Class Constants
|
||||
static readonly relative_dates = [
|
||||
// Start and end are relative offsets, see et2_date.set_min()
|
||||
// or Date objects
|
||||
{
|
||||
value: 'Today',
|
||||
label: 'Today',
|
||||
from(date) {return date;},
|
||||
to(date) {return date;}
|
||||
},
|
||||
{
|
||||
label: 'Yesterday',
|
||||
value: 'Yesterday',
|
||||
from(date)
|
||||
{
|
||||
date.setUTCDate(date.getUTCDate() - 1);
|
||||
return date;
|
||||
},
|
||||
to: ''
|
||||
},
|
||||
{
|
||||
label: 'This week',
|
||||
value: 'This week',
|
||||
from(date) {return egw.week_start(date);},
|
||||
to(date)
|
||||
{
|
||||
date.setUTCDate(date.getUTCDate() + 6);
|
||||
return date;
|
||||
}
|
||||
},
|
||||
{
|
||||
label: '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: '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: '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: '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;
|
||||
}
|
||||
},
|
||||
/*
|
||||
'This quarter'=> array(0,0,0,0, 0,0,0,0), // Just a marker, needs special handling
|
||||
'Last quarter'=> array(0,-4,0,0, 0,-4,0,0), // Just a marker
|
||||
*/
|
||||
{
|
||||
label: '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: '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;
|
||||
}
|
||||
}
|
||||
];
|
||||
|
||||
|
||||
connectedCallback()
|
||||
{
|
||||
super.connectedCallback();
|
||||
this.updateComplete.then(() =>
|
||||
{
|
||||
if(!this.relative)
|
||||
{
|
||||
let options = this._fromNode.getOptions();
|
||||
//@ts-ignore rangePlugin is there, really
|
||||
options.plugins.push(rangePlugin({input: this._toNode.findInputField()}));
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
render()
|
||||
{
|
||||
if(this.relative)
|
||||
{
|
||||
return this.relativeTemplate();
|
||||
}
|
||||
return this.absoluteTemplate();
|
||||
}
|
||||
|
||||
protected relativeTemplate()
|
||||
{
|
||||
return html`
|
||||
<et2-select part="relative" empty_label="${this.empty_label}" placeholder="${this.placeholder}"
|
||||
value="${this.value}">
|
||||
${repeat(this.select_options, (d) => d.value, (o) => this._optionTemplate(o))}
|
||||
</et2-select>
|
||||
`;
|
||||
}
|
||||
|
||||
_optionTemplate(option : SelectOption) : TemplateResult
|
||||
{
|
||||
let icon = option.icon ? html`
|
||||
<et2-image slot="prefix" part="icon" style="width: var(--icon-width)"
|
||||
src="${option.icon}"></et2-image>` : "";
|
||||
|
||||
// Tag used must match this.optionTag, but you can't use the variable directly.
|
||||
// Pass option along so SearchMixin can grab it if needed
|
||||
return html`
|
||||
<sl-menu-item value="${option.value}"
|
||||
title="${!option.title || this.noLang ? option.title : this.egw().lang(option.title)}"
|
||||
class="${option.class}" .option=${option}>
|
||||
${icon}
|
||||
${this.egw().lang(option.label)}
|
||||
</sl-menu-item>`;
|
||||
}
|
||||
|
||||
protected absoluteTemplate()
|
||||
{
|
||||
return html`
|
||||
<et2-date part="from" id="from"></et2-date>
|
||||
<et2-date part="to" id="to"></et2-date>`;
|
||||
}
|
||||
|
||||
get select_options() : SelectOption[]
|
||||
{
|
||||
// @ts-ignore
|
||||
const options = super.select_options || [];
|
||||
// make sure result is unique
|
||||
|
||||
return [...new Map([...options, ...(Et2DateRange.relative_dates || [])].map(item =>
|
||||
[item.value, item])).values()];
|
||||
|
||||
}
|
||||
|
||||
get value() : string | string[] | { from : string; to : string }
|
||||
{
|
||||
return this.relative ?
|
||||
(this._selectNode?.value || this.__value) :
|
||||
{from: <string>this._fromNode?.value, to: <string>this._toNode?.value};
|
||||
}
|
||||
|
||||
set value(new_value : string | string[] | { from : string, to : string })
|
||||
{
|
||||
let oldValue = this.value;
|
||||
if(!new_value || new_value == null || typeof new_value == "undefined")
|
||||
{
|
||||
this._selectNode.value = '';
|
||||
this._fromNode.value = null;
|
||||
this._toNode.value = null;
|
||||
}
|
||||
// Relative
|
||||
else if(new_value && typeof new_value === 'string')
|
||||
{
|
||||
this._set_relative_value(new_value);
|
||||
}
|
||||
else if(new_value && typeof new_value.from === 'undefined' && new_value[0])
|
||||
{
|
||||
new_value = {
|
||||
from: new_value[0],
|
||||
to: new_value[1] || ''
|
||||
};
|
||||
}
|
||||
if(new_value && new_value.from && new_value.to)
|
||||
{
|
||||
this._fromNode._instance.setDate([new_value.from, new_value.to], false);
|
||||
}
|
||||
}
|
||||
|
||||
_set_relative_value(_value)
|
||||
{
|
||||
|
||||
// Show description
|
||||
this.__value = _value;
|
||||
/*
|
||||
let tempDate = new Date();
|
||||
let today = new Date(tempDate.getFullYear(), tempDate.getMonth(), tempDate.getDate(), 0, -tempDate.getTimezoneOffset(), 0);
|
||||
|
||||
// Use strings to avoid references
|
||||
this._fromNode.value = today.toJSON();
|
||||
this._toNode.value = today.toJSON();
|
||||
|
||||
let relative = null;
|
||||
for(var index in Et2DateRange.relative_dates)
|
||||
{
|
||||
if(Et2DateRange.relative_dates[index].value === _value)
|
||||
{
|
||||
relative = Et2DateRange.relative_dates[index];
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(relative)
|
||||
{
|
||||
let dates = ["from", "to"];
|
||||
let value = today.toJSON();
|
||||
for(let i = 0; i < dates.length; i++)
|
||||
{
|
||||
let date = dates[i];
|
||||
if(typeof relative[date] == "function")
|
||||
{
|
||||
value = relative[date](new Date(value));
|
||||
}
|
||||
else
|
||||
{
|
||||
value = this[date]._relativeDate(relative[date]);
|
||||
}
|
||||
this["_" + date + "Node"].value = value;
|
||||
}
|
||||
}
|
||||
|
||||
*/
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the node where we're putting the options
|
||||
*
|
||||
* @returns {HTMLElement}
|
||||
*/
|
||||
get _optionTargetNode() : HTMLElement
|
||||
{
|
||||
return this._selectNode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Render select_options as child DOM Nodes
|
||||
* Overridden here because we can do it in the normal way (render())
|
||||
* @protected
|
||||
*/
|
||||
protected _renderOptions()
|
||||
{}
|
||||
|
||||
get _selectNode() : Et2Select
|
||||
{
|
||||
return this.shadowRoot?.querySelector("[part='relative']");
|
||||
}
|
||||
|
||||
get _fromNode() : Et2Date
|
||||
{
|
||||
return this.shadowRoot?.querySelector("[part='from']")
|
||||
}
|
||||
|
||||
get _toNode() : Et2Date
|
||||
{
|
||||
return this.shadowRoot?.querySelector("[part='to']")
|
||||
}
|
||||
}
|
||||
|
||||
customElements.define("et2-date-range", Et2DateRange);
|
@ -101,5 +101,34 @@ export function inputBasicTests(before : Function, test_value : string, value_se
|
||||
// widget returns what we gave it
|
||||
assert.equal(element.get_value(), test_value);
|
||||
});
|
||||
})
|
||||
});
|
||||
|
||||
describe("Required", () =>
|
||||
{
|
||||
beforeEach(async() =>
|
||||
{
|
||||
element = await before();
|
||||
});
|
||||
|
||||
// This is just visually comparing for a difference, no deep inspection
|
||||
it("looks different when required")
|
||||
|
||||
/*
|
||||
Not yet working attempt to have playwright compare visually
|
||||
|
||||
const pre = await page.locator(element.localName).screenshot();
|
||||
|
||||
element.required = true;
|
||||
|
||||
// wait for asychronous changes to the DOM
|
||||
await elementUpdated(<Element><unknown>element);
|
||||
|
||||
|
||||
const post = await page.locator(element.localName).screenshot();
|
||||
|
||||
expect(post).toMatchSnapshot(pre);
|
||||
|
||||
*/
|
||||
|
||||
});
|
||||
}
|
@ -70,7 +70,14 @@ class Date extends Transformer
|
||||
$form_name = self::form_name($cname, $this->id, $expand);
|
||||
$value =& self::get_array(self::$request->content, $form_name, false, true);
|
||||
|
||||
if($this->type != 'date-duration' && $value)
|
||||
if($this->type == 'et2-date-range')
|
||||
{
|
||||
$value = $this->attrs['relative'] || $this->getElementAttribute($form_name, 'relative') ?
|
||||
$value :
|
||||
['from' => is_array($value) && array_key_exists('from', $value) ? $this->format_date($value['from']) : '',
|
||||
'to' => is_array($value) && array_key_exists('from', $value) ? $this->format_date($value['to']) : ''];
|
||||
}
|
||||
elseif($this->type != 'date-duration' && $value)
|
||||
{
|
||||
$value = $this->format_date($value);
|
||||
}
|
||||
@ -165,6 +172,10 @@ class Date extends Transformer
|
||||
$value = self::get_array($content, $form_name);
|
||||
$valid =& self::get_array($validated, $form_name, true);
|
||||
|
||||
if($value && $this->type == 'et2-date-range')
|
||||
{
|
||||
return $this->validateRange($form_name, $value, $valid);
|
||||
}
|
||||
if($value && $this->type !== 'date-duration')
|
||||
{
|
||||
try
|
||||
@ -282,8 +293,29 @@ class Date extends Transformer
|
||||
//error_log("$this : ($valid)" . Api\DateTime::to($valid));
|
||||
}
|
||||
}
|
||||
|
||||
protected function validateRange($form_name, $value, &$valid)
|
||||
{
|
||||
if($this->attrs['relative'] || $this->getElementAttribute($form_name, "relative"))
|
||||
{
|
||||
$valid = "" . $value;
|
||||
return;
|
||||
}
|
||||
foreach(['from', 'to'] as $field)
|
||||
{
|
||||
if(!$value[$field])
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if(substr($value[$field], -1) === 'Z')
|
||||
{
|
||||
$value[$field] = substr($value[$field], 0, -1);
|
||||
}
|
||||
$valid[$field] = new Api\DateTime($value[$field]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
\EGroupware\Api\Etemplate\Widget::registerWidget(__NAMESPACE__ . '\\Date',
|
||||
array('et2-date', 'et2-date-time', 'time_or_date')
|
||||
array('et2-date', 'et2-date-time', 'et2-date-range', 'time_or_date')
|
||||
);
|
Loading…
Reference in New Issue
Block a user