diff --git a/package.json b/package.json
index 252c2d6..b4375b6 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "@tp/tp-date-input",
- "version": "0.1.0",
+ "version": "0.2.0",
"description": "",
"main": "tp-date-input.js",
"scripts": {
diff --git a/tp-date-input.js b/tp-date-input.js
index fffc4eb..8fc9e7b 100644
--- a/tp-date-input.js
+++ b/tp-date-input.js
@@ -10,6 +10,8 @@ import { EventHelpers } from '@tp/helpers/event-helpers.js';
import { FormElement } from '@tp/helpers/form-element.js';
import { LitElement, html, css } from 'lit';
import { format, parse, parseISO, isAfter, isValid, endOfDay } from 'date-fns/esm';
+import { zonedTimeToUtc } from 'date-fns-tz/esm';
+import { closest } from '@tp/helpers';
class TpDateInput extends EventHelpers(ControlState(FormElement(LitElement))) {
static get styles() {
@@ -17,15 +19,16 @@ class TpDateInput extends EventHelpers(ControlState(FormElement(LitElement))) {
css`
:host {
display: block;
+ position: relative;
+ font-size: 14px;
}
.wrap {
display: flex;
- flex-direction: column;
- }
-
- .wrap > div {
- align-self: flex-end;
+ flex-direction: row;
+ align-items: center;
+ border-radius: 2px;
+ border: solid 1px #000;
}
tp-input {
@@ -34,49 +37,73 @@ class TpDateInput extends EventHelpers(ControlState(FormElement(LitElement))) {
border: none;
}
- tp-input.bigger {
+ tp-input.year {
width: 50px;
}
+ tp-input::part(wrap) {
+ border: none;
+ }
+
.under {
position: relative;
}
.error-message {
position: absolute;
- top: -5px;
+ z-index: 1;
left: 0;
+ right: 0;
font-size: 10px;
color: var(--tp-input-text-color-invalid, #B71C1C);
transition: opacity 0.3s;
opacity: 0;
- will-change: opacity;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+ pointer-events: none;
}
:host([invalid]) .error-message {
opacity: 1;
}
+
+ div[part="delimiter"] {
+ padding-bottom: 2px;
+ }
+
+ .more {
+ flex: 1;
+ display: flex;
+ justify-content: end;
+ }
`
];
}
render() {
- const { _delemiter } = this;
+ const { delimiter, autoValidate, readonly, required, errorMessage } = this;
return html`
-
-
+
+ ${errorMessage ? html`
+ ${errorMessage}
+ ` : null}
`;
}
@@ -115,6 +142,13 @@ class TpDateInput extends EventHelpers(ControlState(FormElement(LitElement))) {
// Set it to `today` to automatically allow dates till today (inclusive).
// Expects 'today' or a ISO Date string.
maxDate: { type: String },
+
+ // List of allowed dates. Any other dates will make the validation fail.
+ allowedDates: { type: Array },
+
+ timeZone: { type: String },
+
+ delimiter: { type: String },
};
}
@@ -127,6 +161,8 @@ class TpDateInput extends EventHelpers(ControlState(FormElement(LitElement))) {
this.optional = false;
this.maxYear = 10;
this.minYear = 10;
+ this.allowedDates = [];
+ this._formatChanged();
}
shouldUpdate(changes) {
@@ -142,7 +178,24 @@ class TpDateInput extends EventHelpers(ControlState(FormElement(LitElement))) {
}
firstUpdated() {
+ super.firstUpdated();
this.listen(this, 'input', '_autoMoveCursor');
+
+ const datepicker = this.querySelector('tp-datepicker');
+ if (datepicker) {
+ datepicker.addEventListener('value-changed', e => {
+ this.value = e.detail;
+ const popup = closest(datepicker, 'tp-popup');
+
+ if (popup) {
+ popup.close();
+ }
+ });
+ }
+ }
+
+ get inputs() {
+ return this.shadowRoot.querySelectorAll('tp-input');
}
/**
@@ -176,19 +229,16 @@ class TpDateInput extends EventHelpers(ControlState(FormElement(LitElement))) {
/**
* Test is a date is selectable when considering all restricting options
- * of the control, like enabledDates, maxDate, ...
+ * of the control, like allowedDates, maxDate, ...
*/
- dateValid(date, maxDate, enabledDates) {
+ dateValid(date, maxDate = this.maxDate, allowedDates = this.allowedDates) {
date = this._toDate(date);
if (isValid(date) === false) {
return false;
}
- maxDate = maxDate || this.maxDate;
- enabledDates = enabledDates || [];
-
- if ((enabledDates.length > 0 && enabledDates.indexOf(date) === -1) ||
+ if ((allowedDates.length > 0 && allowedDates.indexOf(date) === -1) ||
(maxDate && isAfter(date, maxDate))) {
return false;
}
@@ -202,7 +252,7 @@ class TpDateInput extends EventHelpers(ControlState(FormElement(LitElement))) {
this._input0 = '';
this._input1 = '';
this._input2 = '';
- this.shadowRoot.querySelectorAll('era-input').forEach(el => el.invalid = false);
+ this.inputs.forEach(el => el.invalid = false);
this.invalid = false;
this.value = null;
@@ -212,9 +262,14 @@ class TpDateInput extends EventHelpers(ControlState(FormElement(LitElement))) {
}
_inputChanged() {
- const i0 = this._input0;
- const i1 = this._input1;
- const i2 = this._input2;
+ this._skipOnValueChanged = true;
+ setTimeout(() => {
+ this._skipOnValueChanged = false;
+ });
+
+ const i0 = this.inputs[0].value;
+ const i1 = this.inputs[1].value;
+ const i2 = this.inputs[2].value;
if (i0 === '' && i1 === '' && i2 === '' && this.optional) {
this.date = null;
@@ -225,7 +280,7 @@ class TpDateInput extends EventHelpers(ControlState(FormElement(LitElement))) {
if (
i0 === '' || i1 === '' || i2 === '' ||
- !this.inputs[0].validate() || !this.inputs[1].validate() || !this.inputs[2].validate()
+ !(this.inputs[0].validate() && this.inputs[1].validate() && this.inputs[2].validate())
) {
if (this.focused) {
this.date = null;
@@ -243,9 +298,10 @@ class TpDateInput extends EventHelpers(ControlState(FormElement(LitElement))) {
this.inputs[0].invalid = false;
this.inputs[1].invalid = false;
this.inputs[2].invalid = false;
- this.date = date;
- this.value = date.toISOString();
+ this.date = this.timeZone ? zonedTimeToUtc(date, this.timeZone) : date;
+ this.value = this.date.toISOString();
this.invalid = false;
+ this.dispatchEvent(new CustomEvent('value-changed', { detail: this.value, bubbles: true, composed: true }));
} else {
this.inputs[0].invalid = true;
this.inputs[1].invalid = true;
@@ -269,7 +325,7 @@ class TpDateInput extends EventHelpers(ControlState(FormElement(LitElement))) {
}
}
- _validateDay(value) {
+ _validateDay(el, value) {
if (typeof value !== 'string') {
return false;
}
@@ -282,7 +338,7 @@ class TpDateInput extends EventHelpers(ControlState(FormElement(LitElement))) {
return v >= 1 && v <= 31;
}
- _validateMonth(value) {
+ _validateMonth(el, value) {
if (typeof value !== 'string') {
return false;
}
@@ -295,7 +351,7 @@ class TpDateInput extends EventHelpers(ControlState(FormElement(LitElement))) {
return v >= 1 && v <= 12;
}
- _validateYear(value) {
+ _validateYear(el, value) {
if (typeof value !== 'string') {
return false;
}
@@ -319,17 +375,27 @@ class TpDateInput extends EventHelpers(ControlState(FormElement(LitElement))) {
}
}
- _formatChanged(format) {
- if (!format) return;
+ _setClass(idx) {
+ switch (this._inputAssign[idx]) {
+ case 'dd':
+ case 'MM':
+ return '';
+ case 'y':
+ return 'year';
+ }
+ }
- let _realFormat = format;
+ _formatChanged() {
+ if (!this.format) return;
+
+ let _realFormat = this.format;
const types = ['MM', 'dd', 'y'];
this._inputAssign = [];
- this._delimiter = this._determineDelimiter(_realFormat);
+ this.delimiter = this._determineDelimiter(_realFormat);
- const parts = _realFormat.split(this._delimiter);
+ const parts = _realFormat.split(this.delimiter);
if (parts.length < 2) {
console.warn(this.tagName + ': Unknown format. Fallback to format MM-dd-y');
this.format = 'MM-dd-y';
@@ -366,9 +432,8 @@ class TpDateInput extends EventHelpers(ControlState(FormElement(LitElement))) {
// Reset invalid state if value was changed.
// This clears up old invalid states if the value was changed programmatically.
_onValueChanged() {
- // If the control is focused we ignore a programmatically set value because
- // the user may works with the element right now.
- if (this.focused) {
+ // We skip if the user was just inputting values.
+ if (this._skipOnValueChanged) {
return;
}
@@ -409,7 +474,7 @@ class TpDateInput extends EventHelpers(ControlState(FormElement(LitElement))) {
}
_autoMoveCursor(e) {
- const target = e.composedPath().find(node => node.tagName === 'ERA-INPUT');
+ const target = e.composedPath().find(node => node.tagName === 'TP-INPUT');
const idx = Array.from(this.inputs).findIndex(node => node === target);
if (target.value.length === 2 && idx < 2) {
this.inputs[idx + 1].select();