diff --git a/readme.md b/readme.md index 167d79b..f7eda81 100644 --- a/readme.md +++ b/readme.md @@ -455,15 +455,15 @@ Dynamic list input for managing arrays of typed values. ``` #### `DeesInputDatepicker` -Date and time picker component with calendar interface. +Date and time picker component with calendar interface and manual typing support. ```typescript html` diff --git a/ts_web/elements/dees-input-datepicker.ts b/ts_web/elements/dees-input-datepicker.ts index b3fd796..2b00789 100644 --- a/ts_web/elements/dees-input-datepicker.ts +++ b/ts_web/elements/dees-input-datepicker.ts @@ -35,7 +35,7 @@ export class DeesInputDatepicker extends DeesInputBase { public minuteIncrement: number = 1; @property({ type: String }) - public dateFormat: string = 'DD/MM/YYYY'; + public dateFormat: string = 'YYYY-MM-DD'; @property({ type: String }) public minDate: string = ''; @@ -50,7 +50,7 @@ export class DeesInputDatepicker extends DeesInputBase { public weekStartsOn: 0 | 1 = 1; // Default to Monday @property({ type: String }) - public placeholder: string = 'Select date'; + public placeholder: string = 'YYYY-MM-DD'; @state() private isOpened: boolean = false; @@ -479,7 +479,8 @@ export class DeesInputDatepicker extends DeesInputBase { ?disabled=${this.disabled} @click=${this.toggleCalendar} @keydown=${this.handleKeydown} - readonly + @input=${this.handleManualInput} + @blur=${this.handleInputBlur} style="padding-right: ${this.value ? '64px' : '40px'}" />
@@ -641,10 +642,11 @@ export class DeesInputDatepicker extends DeesInputBase { const month = (date.getMonth() + 1).toString().padStart(2, '0'); const year = date.getFullYear().toString(); - formatted = formatted.replace('DD', day); - formatted = formatted.replace('MM', month); + // Replace in correct order to avoid conflicts formatted = formatted.replace('YYYY', year); formatted = formatted.replace('YY', year.slice(-2)); + formatted = formatted.replace('MM', month); + formatted = formatted.replace('DD', day); // Time formatting if enabled if (this.enableTime) { @@ -894,6 +896,117 @@ export class DeesInputDatepicker extends DeesInputBase { this.changeSubject.next(this); } + private handleManualInput(e: InputEvent): void { + const input = e.target as HTMLInputElement; + const inputValue = input.value.trim(); + + if (!inputValue) { + // Clear the value if input is empty + this.value = ''; + this.selectedDate = null; + return; + } + + const parsedDate = this.parseManualDate(inputValue); + if (parsedDate && !isNaN(parsedDate.getTime())) { + // Update internal state without triggering re-render of input + this.value = parsedDate.toISOString(); + this.selectedDate = parsedDate; + this.viewDate = new Date(parsedDate); + this.selectedHour = parsedDate.getHours(); + this.selectedMinute = parsedDate.getMinutes(); + this.changeSubject.next(this); + } + } + + private handleInputBlur(e: FocusEvent): void { + const input = e.target as HTMLInputElement; + const inputValue = input.value.trim(); + + if (!inputValue) { + this.value = ''; + this.selectedDate = null; + this.changeSubject.next(this); + return; + } + + const parsedDate = this.parseManualDate(inputValue); + if (parsedDate && !isNaN(parsedDate.getTime())) { + this.value = parsedDate.toISOString(); + this.selectedDate = parsedDate; + this.viewDate = new Date(parsedDate); + this.selectedHour = parsedDate.getHours(); + this.selectedMinute = parsedDate.getMinutes(); + this.changeSubject.next(this); + // Update the input with formatted date + input.value = this.formatDate(this.value); + } else { + // Revert to previous valid value on blur if parsing failed + input.value = this.formatDate(this.value); + } + } + + private parseManualDate(input: string): Date | null { + if (!input) return null; + + // Split date and time parts if present + const parts = input.split(' '); + let datePart = parts[0]; + let timePart = parts[1] || ''; + + let parsedDate: Date | null = null; + + // Try different date formats + // Format 1: YYYY-MM-DD (ISO-like) + const isoMatch = datePart.match(/^(\d{4})-(\d{1,2})-(\d{1,2})$/); + if (isoMatch) { + const [_, year, month, day] = isoMatch; + parsedDate = new Date(parseInt(year), parseInt(month) - 1, parseInt(day)); + } + + // Format 2: DD.MM.YYYY (European) + if (!parsedDate) { + const euMatch = datePart.match(/^(\d{1,2})\.(\d{1,2})\.(\d{4})$/); + if (euMatch) { + const [_, day, month, year] = euMatch; + parsedDate = new Date(parseInt(year), parseInt(month) - 1, parseInt(day)); + } + } + + // Format 3: MM/DD/YYYY (US) + if (!parsedDate) { + const usMatch = datePart.match(/^(\d{1,2})\/(\d{1,2})\/(\d{4})$/); + if (usMatch) { + const [_, month, day, year] = usMatch; + parsedDate = new Date(parseInt(year), parseInt(month) - 1, parseInt(day)); + } + } + + // If no date was parsed, return null + if (!parsedDate || isNaN(parsedDate.getTime())) { + return null; + } + + // Parse time if present (HH:MM format) + if (timePart) { + const timeMatch = timePart.match(/^(\d{1,2}):(\d{2})$/); + if (timeMatch) { + const [_, hours, minutes] = timeMatch; + parsedDate.setHours(parseInt(hours)); + parsedDate.setMinutes(parseInt(minutes)); + } + } else if (!this.enableTime) { + // If time is not enabled and not provided, use current time + const now = new Date(); + parsedDate.setHours(now.getHours()); + parsedDate.setMinutes(now.getMinutes()); + parsedDate.setSeconds(0); + parsedDate.setMilliseconds(0); + } + + return parsedDate; + } + public getValue(): string { return this.value; }