This commit is contained in:
Juergen Kunz
2025-06-30 11:18:30 +00:00
parent 911c51d078
commit aae4427281
2 changed files with 193 additions and 4 deletions

View File

@ -97,6 +97,43 @@ export const demoFunc = () => html`
</dees-panel> </dees-panel>
</dees-demowrapper> </dees-demowrapper>
<dees-demowrapper .runAfterRender=${async (elementArg: HTMLElement) => {
// Demonstrate timezone functionality
const timezonePickers = elementArg.querySelectorAll('dees-input-datepicker');
timezonePickers.forEach((picker) => {
picker.addEventListener('change', (event: CustomEvent) => {
const target = event.target as DeesInputDatepicker;
console.log(`${target.label} value:`, target.value);
const input = target.shadowRoot?.querySelector('.date-input') as HTMLInputElement;
if (input) {
console.log(`${target.label} formatted:`, input.value);
}
});
});
}}>
<dees-panel .title=${'Timezone Support'} .subtitle=${'Date and time selection with timezone awareness'}>
<dees-input-datepicker
label="Meeting Time (with Timezone)"
description="Select a date/time and timezone for the meeting"
.enableTime=${true}
.enableTimezone=${true}
timeFormat="24h"
timezone="America/New_York"
></dees-input-datepicker>
<dees-input-datepicker
label="Global Event Schedule"
description="Schedule an event across different timezones"
.enableTime=${true}
.enableTimezone=${true}
timeFormat="12h"
timezone="Europe/London"
.minuteIncrement=${30}
></dees-input-datepicker>
</dees-panel>
</dees-demowrapper>
<dees-demowrapper .runAfterRender=${async (elementArg: HTMLElement) => { <dees-demowrapper .runAfterRender=${async (elementArg: HTMLElement) => {
// Demonstrate date constraints // Demonstrate date constraints
const futureDatePicker = elementArg.querySelector('dees-input-datepicker'); const futureDatePicker = elementArg.querySelector('dees-input-datepicker');

View File

@ -52,6 +52,12 @@ export class DeesInputDatepicker extends DeesInputBase<DeesInputDatepicker> {
@property({ type: String }) @property({ type: String })
public placeholder: string = 'YYYY-MM-DD'; public placeholder: string = 'YYYY-MM-DD';
@property({ type: Boolean })
public enableTimezone: boolean = false;
@property({ type: String })
public timezone: string = Intl.DateTimeFormat().resolvedOptions().timeZone;
@state() @state()
private isOpened: boolean = false; private isOpened: boolean = false;
@ -114,7 +120,8 @@ export class DeesInputDatepicker extends DeesInputBase<DeesInputDatepicker> {
border-color: ${cssManager.bdTheme('hsl(222.2 47.4% 11.2%)', 'hsl(210 20% 98%)')}; border-color: ${cssManager.bdTheme('hsl(222.2 47.4% 11.2%)', 'hsl(210 20% 98%)')};
outline: 2px solid transparent; outline: 2px solid transparent;
outline-offset: 2px; outline-offset: 2px;
box-shadow: 0 0 0 2px ${cssManager.bdTheme('hsl(0 0% 100%)', 'hsl(224 71.4% 4.1%)')}, 0 0 0 4px ${cssManager.bdTheme('hsl(222.2 47.4% 11.2% / 0.1)', 'hsl(210 20% 98% / 0.1)')}; box-shadow: 0 0 0 2px ${cssManager.bdTheme('hsl(0 0% 100%)', 'hsl(224 71.4% 4.1%)')},
0 0 0 4px ${cssManager.bdTheme('hsl(222.2 47.4% 11.2% / 0.1)', 'hsl(210 20% 98% / 0.1)')};
} }
.date-input:disabled { .date-input:disabled {
@ -451,9 +458,71 @@ export class DeesInputDatepicker extends DeesInputBase<DeesInputDatepicker> {
.clear-button:active { .clear-button:active {
background: ${cssManager.bdTheme('hsl(0 72.2% 50.6% / 0.2)', 'hsl(0 62.8% 30.6% / 0.2)')}; background: ${cssManager.bdTheme('hsl(0 72.2% 50.6% / 0.2)', 'hsl(0 62.8% 30.6% / 0.2)')};
} }
/* Timezone selector */
.timezone-selector {
margin-top: 12px;
padding-top: 12px;
border-top: 1px solid ${cssManager.bdTheme('hsl(214.3 31.8% 91.4%)', 'hsl(217.2 32.6% 17.5%)')};
}
.timezone-selector-title {
font-size: 12px;
font-weight: 500;
margin-bottom: 8px;
color: ${cssManager.bdTheme('hsl(220 8.9% 46.1%)', 'hsl(215 20.2% 65.1%)')};
}
.timezone-select {
width: 100%;
height: 36px;
border: 1px solid ${cssManager.bdTheme('hsl(214.3 31.8% 91.4%)', 'hsl(217.2 32.6% 17.5%)')};
border-radius: 6px;
padding: 0 12px;
font-size: 14px;
background: ${cssManager.bdTheme('hsl(0 0% 100%)', 'hsl(224 71.4% 4.1%)')};
color: ${cssManager.bdTheme('hsl(224 71.4% 4.1%)', 'hsl(210 20% 98%)')};
cursor: pointer;
transition: all 0.2s ease;
}
.timezone-select:hover {
border-color: ${cssManager.bdTheme('hsl(214.3 31.8% 91.4%)', 'hsl(217.2 32.6% 17.5%)')};
background: ${cssManager.bdTheme('hsl(210 20% 98%)', 'hsl(215 27.9% 16.9%)')};
}
.timezone-select:focus {
outline: none;
border-color: ${cssManager.bdTheme('hsl(222.2 47.4% 11.2%)', 'hsl(210 20% 98%)')};
box-shadow: 0 0 0 2px ${cssManager.bdTheme('hsl(222.2 47.4% 11.2% / 0.1)', 'hsl(210 20% 98% / 0.1)')};
}
`, `,
]; ];
private getTimezones(): { value: string; label: string }[] {
// Common timezones with their display names
return [
{ value: 'UTC', label: 'UTC (Coordinated Universal Time)' },
{ value: 'America/New_York', label: 'Eastern Time (US & Canada)' },
{ value: 'America/Chicago', label: 'Central Time (US & Canada)' },
{ value: 'America/Denver', label: 'Mountain Time (US & Canada)' },
{ value: 'America/Los_Angeles', label: 'Pacific Time (US & Canada)' },
{ value: 'America/Phoenix', label: 'Arizona' },
{ value: 'America/Anchorage', label: 'Alaska' },
{ value: 'Pacific/Honolulu', label: 'Hawaii' },
{ value: 'Europe/London', label: 'London' },
{ value: 'Europe/Paris', label: 'Paris' },
{ value: 'Europe/Berlin', label: 'Berlin' },
{ value: 'Europe/Moscow', label: 'Moscow' },
{ value: 'Asia/Dubai', label: 'Dubai' },
{ value: 'Asia/Kolkata', label: 'India Standard Time' },
{ value: 'Asia/Shanghai', label: 'China Standard Time' },
{ value: 'Asia/Tokyo', label: 'Tokyo' },
{ value: 'Australia/Sydney', label: 'Sydney' },
{ value: 'Pacific/Auckland', label: 'Auckland' },
];
}
render(): TemplateResult { render(): TemplateResult {
const monthNames = [ const monthNames = [
'January', 'February', 'March', 'April', 'May', 'June', 'January', 'February', 'March', 'April', 'May', 'June',
@ -466,6 +535,7 @@ export class DeesInputDatepicker extends DeesInputBase<DeesInputDatepicker> {
const days = this.getDaysInMonth(); const days = this.getDaysInMonth();
const isAM = this.selectedHour < 12; const isAM = this.selectedHour < 12;
const timezones = this.getTimezones();
return html` return html`
<div class="input-wrapper"> <div class="input-wrapper">
@ -576,6 +646,24 @@ export class DeesInputDatepicker extends DeesInputBase<DeesInputDatepicker> {
</div> </div>
` : ''} ` : ''}
<!-- Timezone Selector -->
${this.enableTimezone ? html`
<div class="timezone-selector">
<div class="timezone-selector-title">Timezone</div>
<select
class="timezone-select"
.value="${this.timezone}"
@change="${(e: Event) => this.handleTimezoneChange(e)}"
>
${timezones.map(tz => html`
<option value="${tz.value}" ?selected="${tz.value === this.timezone}">
${tz.label}
</option>
`)}
</select>
</div>
` : ''}
<!-- Action Buttons --> <!-- Action Buttons -->
<div class="calendar-actions"> <div class="calendar-actions">
<button class="action-button today-button" @click="${this.selectToday}"> <button class="action-button today-button" @click="${this.selectToday}">
@ -662,6 +750,19 @@ export class DeesInputDatepicker extends DeesInputBase<DeesInputDatepicker> {
} }
} }
// Timezone formatting if enabled
if (this.enableTimezone) {
const formatter = new Intl.DateTimeFormat('en-US', {
timeZoneName: 'short',
timeZone: this.timezone
});
const parts = formatter.formatToParts(date);
const tzPart = parts.find(part => part.type === 'timeZoneName');
if (tzPart) {
formatted += ` ${tzPart.value}`;
}
}
return formatted; return formatted;
} catch { } catch {
return ''; return '';
@ -784,7 +885,7 @@ export class DeesInputDatepicker extends DeesInputBase<DeesInputDatepicker> {
this.selectedMinute this.selectedMinute
); );
this.value = this.selectedDate.toISOString(); this.value = this.formatValueWithTimezone(this.selectedDate);
this.changeSubject.next(this); this.changeSubject.next(this);
if (!this.enableTime) { if (!this.enableTime) {
@ -799,7 +900,7 @@ export class DeesInputDatepicker extends DeesInputBase<DeesInputDatepicker> {
this.selectedHour = today.getHours(); this.selectedHour = today.getHours();
this.selectedMinute = today.getMinutes(); this.selectedMinute = today.getMinutes();
this.value = this.selectedDate.toISOString(); this.value = this.formatValueWithTimezone(this.selectedDate);
this.changeSubject.next(this); this.changeSubject.next(this);
if (!this.enableTime) { if (!this.enableTime) {
@ -874,11 +975,62 @@ export class DeesInputDatepicker extends DeesInputBase<DeesInputDatepicker> {
this.selectedHour, this.selectedHour,
this.selectedMinute this.selectedMinute
); );
this.value = this.selectedDate.toISOString(); this.value = this.formatValueWithTimezone(this.selectedDate);
this.changeSubject.next(this); this.changeSubject.next(this);
} }
} }
private handleTimezoneChange(e: Event): void {
const select = e.target as HTMLSelectElement;
this.timezone = select.value;
this.updateSelectedDateTime();
}
private formatValueWithTimezone(date: Date): string {
if (!this.enableTimezone) {
return date.toISOString();
}
// Format the date with timezone offset
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;
});
// Create ISO-like format with timezone
const isoString = `${dateParts.year}-${dateParts.month}-${dateParts.day}T${dateParts.hour}:${dateParts.minute}:${dateParts.second}`;
// Get timezone offset
const tzOffset = this.getTimezoneOffset(date, this.timezone);
return `${isoString}${tzOffset}`;
}
private getTimezoneOffset(date: Date, timezone: string): string {
// Create a date in the target timezone
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')}`;
}
private handleKeydown(e: KeyboardEvent): void { private handleKeydown(e: KeyboardEvent): void {
if (e.key === 'Enter' || e.key === ' ') { if (e.key === 'Enter' || e.key === ' ') {
e.preventDefault(); e.preventDefault();