diff --git a/ts_web/elements/dees-input-datepicker.demo.ts b/ts_web/elements/dees-input-datepicker.demo.ts index 857bcc7..398d4ef 100644 --- a/ts_web/elements/dees-input-datepicker.demo.ts +++ b/ts_web/elements/dees-input-datepicker.demo.ts @@ -298,6 +298,73 @@ export const demoFunc = () => html` + { + // Generate sample events for the calendar + const today = new Date(); + const currentMonth = today.getMonth(); + const currentYear = today.getFullYear(); + + const sampleEvents = [ + // Current week events + { + date: `${currentYear}-${(currentMonth + 1).toString().padStart(2, '0')}-${today.getDate().toString().padStart(2, '0')}`, + title: "Team Meeting", + type: "info" as const, + count: 2 + }, + { + date: `${currentYear}-${(currentMonth + 1).toString().padStart(2, '0')}-${(today.getDate() + 1).toString().padStart(2, '0')}`, + title: "Project Deadline", + type: "warning" as const + }, + { + date: `${currentYear}-${(currentMonth + 1).toString().padStart(2, '0')}-${(today.getDate() + 2).toString().padStart(2, '0')}`, + title: "Release Day", + type: "success" as const + }, + { + date: `${currentYear}-${(currentMonth + 1).toString().padStart(2, '0')}-${(today.getDate() + 5).toString().padStart(2, '0')}`, + title: "Urgent Fix Required", + type: "error" as const + }, + // Multiple events on one day + { + date: `${currentYear}-${(currentMonth + 1).toString().padStart(2, '0')}-${(today.getDate() + 7).toString().padStart(2, '0')}`, + title: "Multiple Events Today", + type: "info" as const, + count: 5 + }, + // Next month event + { + date: `${currentYear}-${(currentMonth + 2).toString().padStart(2, '0')}-15`, + title: "Future Planning Session", + type: "info" as const + } + ]; + + const picker = elementArg.querySelector('dees-input-datepicker'); + if (picker) { + picker.events = sampleEvents; + console.log('Calendar events loaded:', sampleEvents); + } + }}> + + + +
+ Event Legend:
+ ● Info | + ● Warning | + ● Success | + ● Error
+ Days with more than 3 events show a count badge +
+
+
+ { // Interactive event demonstration const picker = elementArg.querySelector('dees-input-datepicker'); diff --git a/ts_web/elements/dees-input-datepicker.ts b/ts_web/elements/dees-input-datepicker.ts index 4158163..1859263 100644 --- a/ts_web/elements/dees-input-datepicker.ts +++ b/ts_web/elements/dees-input-datepicker.ts @@ -12,6 +12,14 @@ import { demoFunc } from './dees-input-datepicker.demo.js'; import './dees-icon.js'; import './dees-label.js'; +export interface IDateEvent { + date: string; // ISO date string (YYYY-MM-DD) + title?: string; + description?: string; + type?: 'info' | 'warning' | 'success' | 'error'; + count?: number; // Number of events on this day +} + declare global { interface HTMLElementTagNameMap { 'dees-input-datepicker': DeesInputDatepicker; @@ -58,6 +66,9 @@ export class DeesInputDatepicker extends DeesInputBase { @property({ type: String }) public timezone: string = Intl.DateTimeFormat().resolvedOptions().timeZone; + @property({ type: Array }) + public events: IDateEvent[] = []; + @state() private isOpened: boolean = false; @@ -327,6 +338,95 @@ export class DeesInputDatepicker extends DeesInputBase { opacity: 0.3; } + /* Event indicators */ + .day.has-event { + position: relative; + } + + .event-indicator { + position: absolute; + bottom: 4px; + left: 50%; + transform: translateX(-50%); + display: flex; + gap: 2px; + justify-content: center; + } + + .event-dot { + width: 4px; + height: 4px; + border-radius: 50%; + background: ${cssManager.bdTheme('hsl(220 8.9% 46.1%)', 'hsl(215 20.2% 65.1%)')}; + } + + .event-dot.info { + background: ${cssManager.bdTheme('hsl(211 70% 52%)', 'hsl(211 70% 62%)')}; + } + + .event-dot.warning { + background: ${cssManager.bdTheme('hsl(45 90% 45%)', 'hsl(45 90% 55%)')}; + } + + .event-dot.success { + background: ${cssManager.bdTheme('hsl(142 69% 45%)', 'hsl(142 69% 55%)')}; + } + + .event-dot.error { + background: ${cssManager.bdTheme('hsl(0 72% 51%)', 'hsl(0 72% 61%)')}; + } + + .event-count { + position: absolute; + top: 2px; + right: 2px; + min-width: 16px; + height: 16px; + padding: 0 4px; + background: ${cssManager.bdTheme('hsl(0 72% 51%)', 'hsl(0 72% 61%)')}; + color: white; + border-radius: 8px; + font-size: 10px; + font-weight: 600; + display: flex; + align-items: center; + justify-content: center; + line-height: 1; + } + + /* Tooltip for event details */ + .event-tooltip { + position: absolute; + bottom: calc(100% + 8px); + left: 50%; + transform: translateX(-50%); + background: ${cssManager.bdTheme('hsl(0 0% 20%)', 'hsl(0 0% 90%)')}; + color: ${cssManager.bdTheme('hsl(0 0% 100%)', 'hsl(0 0% 0%)')}; + padding: 8px 12px; + border-radius: 6px; + font-size: 12px; + white-space: nowrap; + pointer-events: none; + opacity: 0; + transition: opacity 0.2s ease; + z-index: 10; + box-shadow: 0 2px 8px rgba(0, 0, 0, 0.2); + } + + .event-tooltip::after { + content: ''; + position: absolute; + top: 100%; + left: 50%; + transform: translateX(-50%); + border: 4px solid transparent; + border-top-color: ${cssManager.bdTheme('hsl(0 0% 20%)', 'hsl(0 0% 90%)')}; + } + + .day.has-event:hover .event-tooltip { + opacity: 1; + } + /* Time selector */ .time-selector { margin-top: 12px; @@ -589,13 +689,33 @@ export class DeesInputDatepicker extends DeesInputBase { const isSelected = this.isSelected(day); const isOtherMonth = day.getMonth() !== this.viewDate.getMonth(); const isDisabled = this.isDisabled(day); + const dayEvents = this.getEventsForDate(day); + const hasEvents = dayEvents.length > 0; + const totalEventCount = dayEvents.reduce((sum, event) => sum + (event.count || 1), 0); return html`
${day.getDate()} + ${hasEvents ? html` + ${totalEventCount > 3 ? html` +
${totalEventCount}
+ ` : html` +
+ ${dayEvents.slice(0, 3).map(event => html` +
+ `)} +
+ `} + ${dayEvents[0].title ? html` +
+ ${dayEvents[0].title} + ${totalEventCount > 1 ? html` (+${totalEventCount - 1} more)` : ''} +
+ ` : ''} + ` : ''}
`; })} @@ -876,6 +996,13 @@ export class DeesInputDatepicker extends DeesInputBase { return false; } + private getEventsForDate(date: Date): IDateEvent[] { + if (!this.events || this.events.length === 0) return []; + + const dateStr = `${date.getFullYear()}-${(date.getMonth() + 1).toString().padStart(2, '0')}-${date.getDate().toString().padStart(2, '0')}`; + return this.events.filter(event => event.date === dateStr); + } + private selectDate(date: Date): void { this.selectedDate = new Date( date.getFullYear(),