From 49025783ed171a643237bebf0182f3e6c0563d3a Mon Sep 17 00:00:00 2001 From: pk Date: Fri, 27 Jan 2023 12:46:53 +0100 Subject: [PATCH] Lot of fixes --- package.json | 2 +- tp-date-input.js | 145 ++++++++++++++++++++++++++++++++++------------- 2 files changed, 106 insertions(+), 41 deletions(-) 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` -
- +
+ -
${_delemiter}
- +
${delimiter}
+ -
${_delemiter}
- +
${delimiter}
+ +
+ +
+ ${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();