2025-09-19 15:26:21 +00:00
|
|
|
import {
|
|
|
|
|
customElement,
|
|
|
|
|
type TemplateResult,
|
|
|
|
|
property,
|
|
|
|
|
state,
|
|
|
|
|
} from '@design.estate/dees-element';
|
Refactor import paths for consistency and clarity across multiple components
- Updated import paths in dees-panel, dees-pdf, dees-progressbar, dees-searchbar, dees-shopping-productcard, dees-simple-appdash, dees-speechbubble, dees-statsgrid, dees-table, dees-toast, dees-updater, and dees-windowlayer to use consistent directory structure.
- Created index.ts files for various components to streamline imports and improve modularity.
- Ensured all imports point to the correct subdirectory structure, enhancing maintainability and readability of the codebase.
2025-12-05 10:19:37 +00:00
|
|
|
import { DeesInputBase } from '../dees-input-base/dees-input-base.js';
|
2025-09-19 15:26:21 +00:00
|
|
|
import { demoFunc } from './demo.js';
|
|
|
|
|
import { datepickerStyles } from './styles.js';
|
|
|
|
|
import { renderDatepicker } from './template.js';
|
2026-04-05 00:24:12 +00:00
|
|
|
import { DeesInputDatepickerPopup } from './datepicker-popup.js';
|
2026-01-27 10:57:42 +00:00
|
|
|
import '../../00group-utility/dees-icon/dees-icon.js';
|
|
|
|
|
import '../../00group-layout/dees-label/dees-label.js';
|
2025-09-19 15:26:21 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
|
declare global {
|
|
|
|
|
interface HTMLElementTagNameMap {
|
|
|
|
|
'dees-input-datepicker': DeesInputDatepicker;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@customElement('dees-input-datepicker')
|
|
|
|
|
export class DeesInputDatepicker extends DeesInputBase<DeesInputDatepicker> {
|
|
|
|
|
public static demo = demoFunc;
|
2026-01-27 10:57:42 +00:00
|
|
|
public static demoGroups = ['Input'];
|
2025-09-19 15:26:21 +00:00
|
|
|
|
|
|
|
|
@property({ type: String })
|
2025-11-17 13:27:11 +00:00
|
|
|
accessor value: string = '';
|
2025-09-19 15:26:21 +00:00
|
|
|
|
|
|
|
|
@property({ type: Boolean })
|
2025-11-17 13:27:11 +00:00
|
|
|
accessor enableTime: boolean = false;
|
2025-09-19 15:26:21 +00:00
|
|
|
|
|
|
|
|
@property({ type: String })
|
2025-11-17 13:27:11 +00:00
|
|
|
accessor timeFormat: '24h' | '12h' = '24h';
|
2025-09-19 15:26:21 +00:00
|
|
|
|
|
|
|
|
@property({ type: Number })
|
2025-11-17 13:27:11 +00:00
|
|
|
accessor minuteIncrement: number = 1;
|
2025-09-19 15:26:21 +00:00
|
|
|
|
|
|
|
|
@property({ type: String })
|
2025-11-17 13:27:11 +00:00
|
|
|
accessor dateFormat: string = 'YYYY-MM-DD';
|
2025-09-19 15:26:21 +00:00
|
|
|
|
|
|
|
|
@property({ type: String })
|
2025-11-17 13:27:11 +00:00
|
|
|
accessor minDate: string = '';
|
2025-09-19 15:26:21 +00:00
|
|
|
|
|
|
|
|
@property({ type: String })
|
2025-11-17 13:27:11 +00:00
|
|
|
accessor maxDate: string = '';
|
2025-09-19 15:26:21 +00:00
|
|
|
|
|
|
|
|
@property({ type: Array })
|
2025-11-17 13:27:11 +00:00
|
|
|
accessor disabledDates: string[] = [];
|
2025-09-19 15:26:21 +00:00
|
|
|
|
|
|
|
|
@property({ type: Number })
|
2026-04-05 00:24:12 +00:00
|
|
|
accessor weekStartsOn: 0 | 1 = 1;
|
2025-09-19 15:26:21 +00:00
|
|
|
|
|
|
|
|
@property({ type: String })
|
2025-11-17 13:27:11 +00:00
|
|
|
accessor placeholder: string = 'YYYY-MM-DD';
|
2025-09-19 15:26:21 +00:00
|
|
|
|
|
|
|
|
@property({ type: Boolean })
|
2025-11-17 13:27:11 +00:00
|
|
|
accessor enableTimezone: boolean = false;
|
2025-09-19 15:26:21 +00:00
|
|
|
|
|
|
|
|
@property({ type: String })
|
2025-11-17 13:27:11 +00:00
|
|
|
accessor timezone: string = Intl.DateTimeFormat().resolvedOptions().timeZone;
|
2025-09-19 15:26:21 +00:00
|
|
|
|
|
|
|
|
@property({ type: Array })
|
2026-04-05 00:24:12 +00:00
|
|
|
accessor events: import('./types.js').IDateEvent[] = [];
|
2025-09-19 15:26:21 +00:00
|
|
|
|
|
|
|
|
@state()
|
2025-11-17 13:27:11 +00:00
|
|
|
accessor isOpened: boolean = false;
|
2025-09-19 15:26:21 +00:00
|
|
|
|
|
|
|
|
@state()
|
2025-11-17 13:27:11 +00:00
|
|
|
accessor selectedDate: Date | null = null;
|
2025-09-19 15:26:21 +00:00
|
|
|
|
|
|
|
|
@state()
|
2025-11-17 13:27:11 +00:00
|
|
|
accessor viewDate: Date = new Date();
|
2025-09-19 15:26:21 +00:00
|
|
|
|
|
|
|
|
@state()
|
2025-11-17 13:27:11 +00:00
|
|
|
accessor selectedHour: number = 0;
|
2025-09-19 15:26:21 +00:00
|
|
|
|
|
|
|
|
@state()
|
2025-11-17 13:27:11 +00:00
|
|
|
accessor selectedMinute: number = 0;
|
2025-09-19 15:26:21 +00:00
|
|
|
|
2026-04-05 00:24:12 +00:00
|
|
|
private popupInstance: DeesInputDatepickerPopup | null = null;
|
2025-09-19 15:26:21 +00:00
|
|
|
|
2026-04-05 00:24:12 +00:00
|
|
|
public static styles = datepickerStyles;
|
2025-09-19 15:26:21 +00:00
|
|
|
|
|
|
|
|
public render(): TemplateResult {
|
|
|
|
|
return renderDatepicker(this);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async firstUpdated() {
|
|
|
|
|
if (!this.value) {
|
|
|
|
|
this.value = '';
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (this.value) {
|
|
|
|
|
try {
|
|
|
|
|
const date = new Date(this.value);
|
|
|
|
|
if (!isNaN(date.getTime())) {
|
|
|
|
|
this.selectedDate = date;
|
|
|
|
|
this.viewDate = new Date(date);
|
|
|
|
|
this.selectedHour = date.getHours();
|
|
|
|
|
this.selectedMinute = date.getMinutes();
|
|
|
|
|
}
|
|
|
|
|
} catch {
|
|
|
|
|
// Invalid date
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
const now = new Date();
|
|
|
|
|
this.viewDate = new Date(now);
|
|
|
|
|
this.selectedHour = now.getHours();
|
|
|
|
|
this.selectedMinute = 0;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public formatDate(isoString: string): string {
|
|
|
|
|
if (!isoString) return '';
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
const date = new Date(isoString);
|
|
|
|
|
if (isNaN(date.getTime())) return '';
|
|
|
|
|
|
|
|
|
|
let formatted = this.dateFormat;
|
|
|
|
|
const day = date.getDate().toString().padStart(2, '0');
|
|
|
|
|
const month = (date.getMonth() + 1).toString().padStart(2, '0');
|
|
|
|
|
const year = date.getFullYear().toString();
|
2026-04-05 00:24:12 +00:00
|
|
|
|
2025-09-19 15:26:21 +00:00
|
|
|
formatted = formatted.replace('YYYY', year);
|
|
|
|
|
formatted = formatted.replace('YY', year.slice(-2));
|
|
|
|
|
formatted = formatted.replace('MM', month);
|
|
|
|
|
formatted = formatted.replace('DD', day);
|
|
|
|
|
|
|
|
|
|
if (this.enableTime) {
|
|
|
|
|
const hours24 = date.getHours();
|
|
|
|
|
const hours12 = hours24 === 0 ? 12 : hours24 > 12 ? hours24 - 12 : hours24;
|
|
|
|
|
const minutes = date.getMinutes().toString().padStart(2, '0');
|
|
|
|
|
const ampm = hours24 >= 12 ? 'PM' : 'AM';
|
|
|
|
|
|
|
|
|
|
if (this.timeFormat === '12h') {
|
|
|
|
|
formatted += ` ${hours12}:${minutes} ${ampm}`;
|
|
|
|
|
} else {
|
|
|
|
|
formatted += ` ${hours24.toString().padStart(2, '0')}:${minutes}`;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (this.enableTimezone) {
|
2026-04-05 00:24:12 +00:00
|
|
|
const formatter = new Intl.DateTimeFormat('en-US', { timeZoneName: 'short', timeZone: this.timezone });
|
2025-09-19 15:26:21 +00:00
|
|
|
const parts = formatter.formatToParts(date);
|
|
|
|
|
const tzPart = parts.find(part => part.type === 'timeZoneName');
|
2026-04-05 00:24:12 +00:00
|
|
|
if (tzPart) formatted += ` ${tzPart.value}`;
|
2025-09-19 15:26:21 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return formatted;
|
|
|
|
|
} catch {
|
|
|
|
|
return '';
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public async toggleCalendar(): Promise<void> {
|
|
|
|
|
if (this.disabled) return;
|
|
|
|
|
|
|
|
|
|
if (this.isOpened) {
|
2026-04-05 00:24:12 +00:00
|
|
|
this.closePopup();
|
|
|
|
|
return;
|
2025-09-19 15:26:21 +00:00
|
|
|
}
|
|
|
|
|
|
2026-04-05 00:24:12 +00:00
|
|
|
this.isOpened = true;
|
2025-09-19 15:26:21 +00:00
|
|
|
|
2026-04-05 00:24:12 +00:00
|
|
|
const inputContainer = this.shadowRoot!.querySelector('.input-container') as HTMLElement;
|
|
|
|
|
const rect = inputContainer.getBoundingClientRect();
|
|
|
|
|
const spaceBelow = window.innerHeight - rect.bottom;
|
|
|
|
|
const spaceAbove = rect.top;
|
|
|
|
|
const opensToTop = spaceBelow < 400 && spaceAbove > spaceBelow;
|
2025-09-19 15:26:21 +00:00
|
|
|
|
2026-04-05 00:24:12 +00:00
|
|
|
if (!this.popupInstance) {
|
|
|
|
|
this.popupInstance = new DeesInputDatepickerPopup();
|
2025-09-19 15:26:21 +00:00
|
|
|
}
|
|
|
|
|
|
2026-04-05 00:24:12 +00:00
|
|
|
// Configure popup
|
|
|
|
|
this.popupInstance.triggerRect = rect;
|
|
|
|
|
this.popupInstance.ownerComponent = this;
|
|
|
|
|
this.popupInstance.opensToTop = opensToTop;
|
|
|
|
|
this.popupInstance.enableTime = this.enableTime;
|
|
|
|
|
this.popupInstance.timeFormat = this.timeFormat;
|
|
|
|
|
this.popupInstance.minuteIncrement = this.minuteIncrement;
|
|
|
|
|
this.popupInstance.weekStartsOn = this.weekStartsOn;
|
|
|
|
|
this.popupInstance.minDate = this.minDate;
|
|
|
|
|
this.popupInstance.maxDate = this.maxDate;
|
|
|
|
|
this.popupInstance.disabledDates = this.disabledDates;
|
|
|
|
|
this.popupInstance.enableTimezone = this.enableTimezone;
|
|
|
|
|
this.popupInstance.timezone = this.timezone;
|
|
|
|
|
this.popupInstance.events = this.events;
|
|
|
|
|
this.popupInstance.selectedDate = this.selectedDate;
|
|
|
|
|
this.popupInstance.viewDate = new Date(this.viewDate);
|
|
|
|
|
this.popupInstance.selectedHour = this.selectedHour;
|
|
|
|
|
this.popupInstance.selectedMinute = this.selectedMinute;
|
|
|
|
|
|
|
|
|
|
// Listen for popup events
|
|
|
|
|
this.popupInstance.addEventListener('date-selected', this.handleDateSelected);
|
|
|
|
|
this.popupInstance.addEventListener('date-cleared', this.handleDateCleared);
|
|
|
|
|
this.popupInstance.addEventListener('close-request', this.handleCloseRequest);
|
|
|
|
|
this.popupInstance.addEventListener('reposition-request', this.handleRepositionRequest);
|
|
|
|
|
|
|
|
|
|
await this.popupInstance.show();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private closePopup(): void {
|
|
|
|
|
this.isOpened = false;
|
|
|
|
|
if (this.popupInstance) {
|
|
|
|
|
this.popupInstance.removeEventListener('date-selected', this.handleDateSelected);
|
|
|
|
|
this.popupInstance.removeEventListener('date-cleared', this.handleDateCleared);
|
|
|
|
|
this.popupInstance.removeEventListener('close-request', this.handleCloseRequest);
|
|
|
|
|
this.popupInstance.removeEventListener('reposition-request', this.handleRepositionRequest);
|
|
|
|
|
this.popupInstance.hide();
|
2025-09-19 15:26:21 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-05 00:24:12 +00:00
|
|
|
private handleDateSelected = (event: Event): void => {
|
|
|
|
|
const date = (event as CustomEvent).detail as Date;
|
|
|
|
|
this.selectedDate = date;
|
|
|
|
|
this.selectedHour = date.getHours();
|
|
|
|
|
this.selectedMinute = date.getMinutes();
|
|
|
|
|
this.viewDate = new Date(date);
|
|
|
|
|
this.value = this.formatValueWithTimezone(date);
|
2025-09-19 15:26:21 +00:00
|
|
|
this.changeSubject.next(this);
|
2026-04-05 00:24:12 +00:00
|
|
|
};
|
2025-09-19 15:26:21 +00:00
|
|
|
|
2026-04-05 00:24:12 +00:00
|
|
|
private handleDateCleared = (): void => {
|
2025-09-19 15:26:21 +00:00
|
|
|
this.value = '';
|
|
|
|
|
this.selectedDate = null;
|
|
|
|
|
this.changeSubject.next(this);
|
2026-04-05 00:24:12 +00:00
|
|
|
};
|
2025-09-19 15:26:21 +00:00
|
|
|
|
2026-04-05 00:24:12 +00:00
|
|
|
private handleCloseRequest = (): void => {
|
|
|
|
|
this.closePopup();
|
|
|
|
|
};
|
2025-09-19 15:26:21 +00:00
|
|
|
|
2026-04-05 00:24:12 +00:00
|
|
|
private handleRepositionRequest = (): void => {
|
|
|
|
|
if (!this.popupInstance || !this.isOpened) return;
|
|
|
|
|
const inputContainer = this.shadowRoot!.querySelector('.input-container') as HTMLElement;
|
|
|
|
|
if (!inputContainer) return;
|
2025-09-19 15:26:21 +00:00
|
|
|
|
2026-04-05 00:24:12 +00:00
|
|
|
const rect = inputContainer.getBoundingClientRect();
|
|
|
|
|
if (rect.bottom < 0 || rect.top > window.innerHeight) {
|
|
|
|
|
this.closePopup();
|
|
|
|
|
return;
|
2025-09-19 15:26:21 +00:00
|
|
|
}
|
|
|
|
|
|
2026-04-05 00:24:12 +00:00
|
|
|
const spaceBelow = window.innerHeight - rect.bottom;
|
|
|
|
|
const spaceAbove = rect.top;
|
|
|
|
|
this.popupInstance.opensToTop = spaceBelow < 400 && spaceAbove > spaceBelow;
|
|
|
|
|
this.popupInstance.triggerRect = rect;
|
|
|
|
|
};
|
2025-09-19 15:26:21 +00:00
|
|
|
|
|
|
|
|
public handleKeydown(e: KeyboardEvent): void {
|
|
|
|
|
if (e.key === 'Enter' || e.key === ' ') {
|
|
|
|
|
e.preventDefault();
|
|
|
|
|
this.toggleCalendar();
|
|
|
|
|
} else if (e.key === 'Escape' && this.isOpened) {
|
|
|
|
|
e.preventDefault();
|
2026-04-05 00:24:12 +00:00
|
|
|
this.closePopup();
|
2025-09-19 15:26:21 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public clearValue(e: Event): void {
|
|
|
|
|
e.stopPropagation();
|
|
|
|
|
this.value = '';
|
|
|
|
|
this.selectedDate = null;
|
|
|
|
|
this.changeSubject.next(this);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public handleManualInput(e: InputEvent): void {
|
|
|
|
|
const input = e.target as HTMLInputElement;
|
|
|
|
|
const inputValue = input.value.trim();
|
2026-04-05 00:24:12 +00:00
|
|
|
|
2025-09-19 15:26:21 +00:00
|
|
|
if (!inputValue) {
|
|
|
|
|
this.value = '';
|
|
|
|
|
this.selectedDate = null;
|
|
|
|
|
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);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public handleInputBlur(e: FocusEvent): void {
|
|
|
|
|
const input = e.target as HTMLInputElement;
|
|
|
|
|
const inputValue = input.value.trim();
|
2026-04-05 00:24:12 +00:00
|
|
|
|
2025-09-19 15:26:21 +00:00
|
|
|
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);
|
|
|
|
|
input.value = this.formatDate(this.value);
|
|
|
|
|
} else {
|
|
|
|
|
input.value = this.formatDate(this.value);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private parseManualDate(input: string): Date | null {
|
|
|
|
|
if (!input) return null;
|
|
|
|
|
|
|
|
|
|
const parts = input.split(' ');
|
|
|
|
|
let datePart = parts[0];
|
|
|
|
|
let timePart = parts[1] || '';
|
|
|
|
|
let parsedDate: Date | null = null;
|
|
|
|
|
|
|
|
|
|
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));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-05 00:24:12 +00:00
|
|
|
if (!parsedDate || isNaN(parsedDate.getTime())) return null;
|
2025-09-19 15:26:21 +00:00
|
|
|
|
|
|
|
|
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) {
|
|
|
|
|
const now = new Date();
|
|
|
|
|
parsedDate.setHours(now.getHours());
|
|
|
|
|
parsedDate.setMinutes(now.getMinutes());
|
|
|
|
|
parsedDate.setSeconds(0);
|
|
|
|
|
parsedDate.setMilliseconds(0);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return parsedDate;
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-05 00:24:12 +00:00
|
|
|
private formatValueWithTimezone(date: Date): string {
|
|
|
|
|
if (!this.enableTimezone) return date.toISOString();
|
|
|
|
|
|
|
|
|
|
const formatter = new Intl.DateTimeFormat('en-US', {
|
|
|
|
|
year: 'numeric', month: '2-digit', day: '2-digit',
|
|
|
|
|
hour: '2-digit', minute: '2-digit', second: '2-digit',
|
|
|
|
|
hour12: false, timeZone: this.timezone, timeZoneName: 'short',
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
const parts = formatter.formatToParts(date);
|
|
|
|
|
const dateParts: any = {};
|
|
|
|
|
parts.forEach(part => { dateParts[part.type] = part.value; });
|
|
|
|
|
|
|
|
|
|
const isoString = `${dateParts.year}-${dateParts.month}-${dateParts.day}T${dateParts.hour}:${dateParts.minute}:${dateParts.second}`;
|
|
|
|
|
const tzOffset = this.getTimezoneOffset(date, this.timezone);
|
|
|
|
|
return `${isoString}${tzOffset}`;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private getTimezoneOffset(date: Date, timezone: string): string {
|
|
|
|
|
const tzDate = new Date(date.toLocaleString('en-US', { timeZone: timezone }));
|
|
|
|
|
const utcDate = new Date(date.toLocaleString('en-US', { timeZone: 'UTC' }));
|
|
|
|
|
const offsetMinutes = (tzDate.getTime() - utcDate.getTime()) / (1000 * 60);
|
|
|
|
|
const hours = Math.floor(Math.abs(offsetMinutes) / 60);
|
|
|
|
|
const minutes = Math.abs(offsetMinutes) % 60;
|
|
|
|
|
const sign = offsetMinutes >= 0 ? '+' : '-';
|
|
|
|
|
return `${sign}${hours.toString().padStart(2, '0')}:${minutes.toString().padStart(2, '0')}`;
|
|
|
|
|
}
|
|
|
|
|
|
2025-09-19 15:26:21 +00:00
|
|
|
public getValue(): string {
|
|
|
|
|
return this.value;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public setValue(value: string): void {
|
|
|
|
|
this.value = value;
|
|
|
|
|
if (value) {
|
|
|
|
|
try {
|
|
|
|
|
const date = new Date(value);
|
|
|
|
|
if (!isNaN(date.getTime())) {
|
|
|
|
|
this.selectedDate = date;
|
|
|
|
|
this.viewDate = new Date(date);
|
|
|
|
|
this.selectedHour = date.getHours();
|
|
|
|
|
this.selectedMinute = date.getMinutes();
|
|
|
|
|
}
|
|
|
|
|
} catch {
|
|
|
|
|
// Invalid date
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2026-04-05 00:24:12 +00:00
|
|
|
|
|
|
|
|
async disconnectedCallback() {
|
|
|
|
|
await super.disconnectedCallback();
|
|
|
|
|
if (this.popupInstance) {
|
|
|
|
|
this.popupInstance.removeEventListener('date-selected', this.handleDateSelected);
|
|
|
|
|
this.popupInstance.removeEventListener('date-cleared', this.handleDateCleared);
|
|
|
|
|
this.popupInstance.removeEventListener('close-request', this.handleCloseRequest);
|
|
|
|
|
this.popupInstance.removeEventListener('reposition-request', this.handleRepositionRequest);
|
|
|
|
|
this.popupInstance.hide();
|
|
|
|
|
this.popupInstance = null;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|