simple-calendar-js 2.0.2 → 2.0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -16,13 +16,19 @@ A pure JavaScript calendar component with no dependencies. Features responsive d
16
16
  - **Internationalization** - Full localization support for weekdays, months, and UI labels
17
17
  - **Month/Year Dropdowns** - Quick navigation with clickable month and year selectors
18
18
  - **Color Customization** - Comprehensive theming system with light/dark mode support
19
+ - **Enhanced Theme Detection** - Automatic theme detection for Next.js, Tailwind CSS, and other frameworks
19
20
  - **Theme Integration** - Built-in dark theme with automatic color adaptation
20
21
  - **Enhanced Navigation** - Transparent navigation buttons with theme-aware hover effects
21
22
  - **Always-Visible Time Lines** - Hour and half-hour lines remain visible regardless of grid settings
23
+ - **Header Visibility Control** - Show/hide month, year, navigation, and view buttons independently
24
+ - **Smart Container Optimization** - Automatic layout optimization for small calendar containers
25
+ - **Improved Text Overflow** - Enhanced ellipsis behavior for event text in constrained spaces
26
+ - **Clean Interaction Design** - Removed distracting hover effects for professional appearance
22
27
  - **CSS Prefix** - All classes prefixed with 'sc-' to avoid conflicts
23
28
  - **Template Strings** - Clean HTML generation using template literals
24
29
  - **Event Support** - Add, remove, and display events
25
30
  - **Keyboard Navigation** - Arrow keys, T (today), M/W/D (views)
31
+ - **Memory Management** - Proper cleanup with destroy() method for SPAs
26
32
 
27
33
  ## Quick Start
28
34
 
@@ -73,6 +79,9 @@ const calendar = new SimpleCalendarJs('#calendar', {
73
79
  showWeekButton: true, // true/false - show week view button
74
80
  showDayButton: true, // true/false - show day view button
75
81
  showNavigation: true, // true/false - show navigation buttons (prev/next)
82
+ showTitle: true, // true/false - show entire title container (month/year)
83
+ showMonth: true, // true/false - show month in title (when showTitle is true)
84
+ showYear: true, // true/false - show year in title (when showTitle is true)
76
85
 
77
86
  // Localization Options
78
87
  weekdays: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'],
@@ -286,8 +295,38 @@ colors: {
286
295
  | `todayText` | Today's date text | `#dc3545` | `#ff6b6b` |
287
296
  | `hoverBg` | Hover backgrounds | `#f8f9fa` | `#2d2d2d` |
288
297
 
289
- ### Theme Integration
290
- The calendar automatically detects `data-theme="dark"` on parent elements and applies dark colors accordingly. Perfect for integration with existing website theme systems.
298
+ ### Enhanced Theme Detection
299
+ The calendar features intelligent theme detection covering popular frameworks and systems:
300
+
301
+ **Automatic Detection Priority:**
302
+ 1. **data-theme attribute** - `data-theme="dark"` (original system)
303
+ 2. **Class-based themes** - `.dark` or `.light` classes on `html` or `body` (Next.js, Tailwind CSS)
304
+ 3. **CSS custom properties** - `--theme` variable
305
+ 4. **Alternative attributes** - `theme` attribute
306
+ 5. **System preference** - `prefers-color-scheme: dark`
307
+
308
+ **Framework Compatibility:**
309
+ - **Next.js** with `next-themes` - Automatically detects `.dark` class
310
+ - **Tailwind CSS** - Supports Tailwind's dark mode classes
311
+ - **Custom systems** - Flexible detection for various theme implementations
312
+ - **Real-time updates** - Theme changes are detected and applied automatically
313
+
314
+ **Example Usage:**
315
+ ```javascript
316
+ // Works automatically with Next.js themes
317
+ const calendar = new SimpleCalendarJs('#calendar', {
318
+ // No manual theme configuration needed
319
+ });
320
+
321
+ // Manual override still available
322
+ document.documentElement.setAttribute('data-theme', 'dark');
323
+ ```
324
+
325
+ **Memory Management:**
326
+ The calendar includes proper cleanup with a `destroy()` method for Single Page Applications:
327
+ ```javascript
328
+ calendar.destroy(); // Cleans up theme observers and event listeners
329
+ ```
291
330
 
292
331
  ## Event Format
293
332
 
@@ -414,11 +453,17 @@ calendar.setShowMonthButton(true); // Show/hide month view button
414
453
  calendar.setShowWeekButton(true); // Show/hide week view button
415
454
  calendar.setShowDayButton(true); // Show/hide day view button
416
455
  calendar.setShowNavigation(true); // Show/hide navigation buttons
456
+ calendar.setShowTitle(true); // Show/hide entire title container
457
+ calendar.setShowMonth(true); // Show/hide month in title
458
+ calendar.setShowYear(true); // Show/hide year in title
417
459
  calendar.setViewButtonsVisibility( // Set multiple button visibility at once
418
460
  true, // month button
419
461
  true, // week button
420
462
  false // day button
421
463
  );
464
+
465
+ // Memory management
466
+ calendar.destroy(); // Clean up observers and event listeners
422
467
  ```
423
468
 
424
469
  ## CSS Classes
@@ -568,6 +613,38 @@ In month view with `fulldayMode: false`, event counts are displayed as styled ba
568
613
  - Today's badge uses full event color saturation for emphasis
569
614
  - Seamlessly integrates with custom color schemes
570
615
 
616
+ ## Small Container Optimization
617
+
618
+ The calendar automatically optimizes its layout for small containers and constrained spaces:
619
+
620
+ ### **Container Detection**
621
+ - **Automatic optimization** for calendars with height ≤ 500px
622
+ - **Responsive text sizing** based on container dimensions
623
+ - **Smart padding reduction** in tight spaces
624
+
625
+ ### **Layout Improvements**
626
+ - **Flex/Grid overflow prevention** - Uses `min-height: 0` to allow proper content shrinking
627
+ - **Container hierarchy fixes** - Ensures all nested containers respect parent boundaries
628
+ - **Text ellipsis enhancement** - More aggressive text truncation in small spaces
629
+
630
+ ### **Event Text Optimization**
631
+ - **Fullday mode fixes** - Event text properly constrained within event boundaries
632
+ - **Padding-aware sizing** - Text width accounts for container padding
633
+ - **Responsive font sizes** - Smaller fonts in compact containers
634
+ - **Enhanced ellipsis** - Earlier text truncation for better readability
635
+
636
+ ### **Cross-View Consistency**
637
+ - **Month view** - Days shrink to fit container height
638
+ - **Week view** - Time slots and fullday events scale appropriately
639
+ - **Day view** - All elements respect container constraints
640
+
641
+ Example usage in small spaces:
642
+ ```html
643
+ <div style="height: 400px; width: 300px;">
644
+ <div id="small-calendar"></div>
645
+ </div>
646
+ ```
647
+
571
648
  ## Enhanced Styling Features
572
649
 
573
650
  ### Navigation Button Styling
@@ -585,6 +662,12 @@ In month view with `fulldayMode: false`, event counts are displayed as styled ba
585
662
  - **Proper Hierarchy**: Uses `!important` declarations to ensure visibility
586
663
  - **Theme Integration**: Line colors adapt to current theme automatically
587
664
 
665
+ ### Clean Interaction Design
666
+ - **Removed Hover Effects**: Day cells and time slots no longer show distracting hover backgrounds
667
+ - **Professional Appearance**: Cleaner, more focused user interface
668
+ - **Preserved Functionality**: All click interactions and cursor pointers remain intact
669
+ - **Better Visual Hierarchy**: User attention stays on important content like events and dates
670
+
588
671
  ### Today Highlighting
589
672
  - **Enhanced Contrast**: Today's background is darker in dark mode for better visibility
590
673
  - **Cross-View Consistency**: Today highlighting works consistently across month, week, and day views
@@ -1,5 +1,5 @@
1
1
  /**
2
- * SimpleCalendarJs v2.0.2
2
+ * SimpleCalendarJs v2.0.3
3
3
  * A clean, modern, and feature-rich JavaScript calendar component with zero dependencies
4
4
  *
5
5
  * @author Pedro Lopes <simplecalendarjs@gmail.com>
@@ -7,4 +7,4 @@
7
7
  * @license SEE LICENSE IN LICENSE
8
8
  * @repository https://github.com/pclslopes/SimpleCalendarJs
9
9
  */
10
- .sc-calendar{--sc-bg-primary:#ffffff;--sc-bg-secondary:#f8f9fa;--sc-bg-tertiary:#e9ecef;--sc-text-primary:#212529;--sc-text-secondary:#6c757d;--sc-text-muted:#adb5bd;--sc-border-color:#e9ecef;--sc-border-light:#dee2e6;--sc-accent-color:#4c6f94;--sc-accent-hover:#0056b3;--sc-today-bg:#e3f2fd;--sc-today-text:#dc3545;--sc-hover-bg:#f8f9fa;--sc-shadow:none;--sc-shadow-enabled:0 2px 10px rgba(0, 0, 0, 0.1);font-family:inherit;background:var(--sc-bg-primary);color:var(--sc-text-primary);box-shadow:var(--sc-shadow);overflow:hidden;width:100%;max-width:100%;display:flex;flex-direction:column}.sc-calendar.sc-shadow{--sc-shadow:var(--sc-shadow-enabled)}@media (prefers-color-scheme:dark){.sc-calendar{--sc-bg-primary:#1a1a1a;--sc-bg-secondary:#2d2d2d;--sc-bg-tertiary:#3a3a3a;--sc-text-primary:#ffffff;--sc-text-secondary:#b0b0b0;--sc-text-muted:#888888;--sc-border-color:#444444;--sc-border-light:#555555;--sc-accent-color:#4a90e2;--sc-accent-hover:#357abd;--sc-today-bg:#1a2f4a;--sc-today-text:#ff6b6b;--sc-hover-bg:#2d2d2d;--sc-shadow-enabled:0 2px 10px rgba(0, 0, 0, 0.5)}}[data-theme=dark] .sc-calendar{--sc-bg-primary:#1a1a1a;--sc-bg-secondary:#2d2d2d;--sc-bg-tertiary:#3a3a3a;--sc-text-primary:#ffffff;--sc-text-secondary:#b0b0b0;--sc-text-muted:#888888;--sc-border-color:#444444;--sc-border-light:#555555;--sc-accent-color:#4a90e2;--sc-accent-hover:#357abd;--sc-today-bg:#1a2f4a;--sc-today-text:#ff6b6b;--sc-hover-bg:#2d2d2d;--sc-shadow-enabled:0 2px 10px rgba(0, 0, 0, 0.5)}[data-theme=light] .sc-calendar{--sc-bg-primary:#ffffff;--sc-bg-secondary:#f8f9fa;--sc-bg-tertiary:#e9ecef;--sc-text-primary:#212529;--sc-text-secondary:#6c757d;--sc-text-muted:#adb5bd;--sc-border-color:#e9ecef;--sc-border-light:#dee2e6;--sc-accent-color:#4c6f94;--sc-accent-hover:#0056b3;--sc-today-bg:#e3f2fd;--sc-today-text:#dc3545;--sc-hover-bg:#f8f9fa;--sc-shadow-enabled:0 2px 10px rgba(0, 0, 0, 0.1)}.sc-calendar{background:var(--calendar-bg,var(--sc-bg-primary));color:var(--calendar-text,var(--sc-text-primary));height:100%}.sc-header{background:0 0;padding:16px;display:grid;grid-template-columns:1fr auto 1fr;align-items:center;gap:12px}.sc-borders-both .sc-header,.sc-borders-horizontal .sc-header{border-bottom:1px solid var(--sc-border-color)}.sc-nav{display:flex;align-items:center;gap:8px;justify-self:start}.sc-title-container{position:relative;justify-self:center}.sc-title{margin:0;font-size:1.25rem;font-weight:600;color:var(--sc-text-primary);white-space:nowrap;text-align:center;user-select:none}.sc-title-month,.sc-title-year{cursor:pointer;padding:2px 4px;border-radius:3px;transition:background-color .2s ease}.sc-title-month:hover,.sc-title-year:hover{color:var(--sc-event-color,#4c6f94)}.sc-month-dropdown{position:absolute;top:100%;left:50%;transform:translateX(-50%);background:var(--sc-bg-primary);border:1px solid var(--sc-border-color);border-radius:4px;z-index:1000;min-width:120px;box-shadow:var(--sc-shadow-enabled);max-height:240px;overflow-y:auto}.sc-month-option{padding:4px 8px;font-size:.75rem;color:var(--sc-text-primary);cursor:pointer;transition:background-color .2s ease}.sc-month-option:hover{background:var(--sc-bg-tertiary)}.sc-month-option.sc-current{background:var(--sc-event-color,#4c6f94);color:#fff}.sc-month-option.sc-current:hover{background:color-mix(in srgb,var(--sc-event-color,#4c6f94) 85%,#000)}.sc-year-dropdown{position:absolute;top:100%;right:-20px;background:var(--sc-bg-primary);border:1px solid var(--sc-border-color);border-radius:4px;z-index:1000;min-width:100px;box-shadow:var(--sc-shadow-enabled);max-height:240px;overflow-y:auto}.sc-year-option{padding:4px 8px;font-size:.75rem;color:var(--sc-text-primary);cursor:pointer;transition:background-color .2s ease;text-align:center}.sc-year-option:hover{background:var(--sc-bg-tertiary)}.sc-year-option.sc-current{background:var(--sc-event-color,#4c6f94);color:#fff}.sc-year-option.sc-current:hover{background:color-mix(in srgb,var(--sc-event-color,#4c6f94) 85%,#000)}.sc-btn{background:0 0;border:1px solid var(--sc-border-light);border-radius:4px;padding:8px 12px;cursor:pointer;color:var(--sc-text-primary);font-size:.875rem;font-weight:500;transition:all .2s ease;min-width:36px;display:flex;align-items:center;justify-content:center}.sc-nav .sc-btn{background:0 0;border:none;font-size:1.2rem;padding:8px;min-width:32px;color:var(--sc-text-primary);font-weight:700}.sc-btn:hover{background:var(--sc-bg-tertiary);border-color:#adb5bd}.sc-nav .sc-btn:hover{background:var(--sc-bg-tertiary);color:var(--sc-text-primary)}.sc-btn:active{background:#dee2e6}.sc-nav .sc-btn:active{background:var(--sc-bg-tertiary)}.sc-view-switcher{display:flex;gap:4px;justify-self:end}.sc-view-btn.sc-active{background:var(--sc-event-color,#4c6f94);color:#fff;border-color:var(--sc-event-color,#4c6f94)}.sc-view-btn.sc-active:hover{background:color-mix(in srgb,var(--sc-event-color,#4c6f94) 85%,#000);border-color:color-mix(in srgb,var(--sc-event-color,#4c6f94) 85%,#000)}.sc-content{flex:1;height:100%;display:flex;flex-direction:column}.sc-view-container{flex:1;overflow:hidden;height:100%}.sc-month-view{height:100%;display:flex;flex-direction:column}.sc-weekdays{display:grid;grid-template-columns:repeat(7,1fr);background:var(--sc-bg-secondary)}.sc-weekday{padding:12px 8px;text-align:center;font-weight:600;font-size:.875rem;color:var(--sc-text-secondary)}.sc-borders-both .sc-month-view .sc-weekdays,.sc-borders-vertical .sc-month-view .sc-weekdays{border-left:1px solid var(--sc-border-color);border-right:1px solid var(--sc-border-color)}.sc-borders-both .sc-month-view .sc-weekday,.sc-borders-vertical .sc-month-view .sc-weekday{border-right:1px solid var(--sc-border-color)}.sc-borders-both .sc-month-view .sc-weekday:nth-child(7n),.sc-borders-vertical .sc-month-view .sc-weekday:nth-child(7n){border-right:none}.sc-days-grid{display:grid;grid-template-columns:repeat(7,1fr);grid-template-rows:repeat(6,1fr);flex:1;overflow:visible}.sc-month-view.sc-fullday .sc-days-grid{grid-template-rows:repeat(6,auto);min-height:auto}.sc-month-view.sc-fullday .sc-day{display:flex;flex-direction:column;position:relative}.sc-day{display:flex;flex-direction:column;min-height:80px;cursor:pointer;transition:background-color .2s ease;overflow:visible}.sc-day:hover{background:var(--sc-hover-bg)}.sc-day-number{text-align:center;font-weight:500;margin-bottom:4px;font-size:.875rem;line-height:1.2}.sc-day.sc-other-month .sc-day-number{color:var(--sc-text-muted)}.sc-day.sc-today{background:var(--sc-today-bg)}.sc-day.sc-today .sc-day-number{color:var(--sc-today-text);font-weight:600}.sc-day-count-mode{display:flex;align-items:center;justify-content:center}.sc-day-content{text-align:center;display:flex;flex-direction:column;align-items:center;justify-content:center}.sc-day-count-mode .sc-day-number{font-size:1.125rem;font-weight:600;margin-bottom:4px}.sc-event-count{font-size:.7rem;font-weight:600;line-height:1.2;background:color-mix(in srgb,var(--sc-event-color,#4c6f94) 15%,var(--sc-bg-tertiary));color:color-mix(in srgb,var(--sc-event-color,#4c6f94) 80%,#000);padding:2px 6px;border-radius:10px;display:inline-block;margin-top:2px;text-align:center;box-sizing:border-box}@media (prefers-color-scheme:light){.sc-event-count{background:color-mix(in srgb,var(--sc-event-color,#4c6f94) 15%,var(--sc-bg-tertiary));color:color-mix(in srgb,var(--sc-event-color,#4c6f94) 80%,#000)}}[data-theme=light] .sc-event-count{background:color-mix(in srgb,var(--sc-event-color,#4c6f94) 15%,var(--sc-bg-tertiary));color:color-mix(in srgb,var(--sc-event-color,#4c6f94) 80%,#000)}@media (prefers-color-scheme:dark){.sc-event-count{background:color-mix(in srgb,var(--sc-event-color,#4c6f94) 20%,var(--sc-bg-tertiary));color:color-mix(in srgb,var(--sc-event-color,#4c6f94) 30%,#fff)}}[data-theme=dark] .sc-event-count{background:color-mix(in srgb,var(--sc-event-color,#4c6f94) 20%,var(--sc-bg-tertiary));color:color-mix(in srgb,var(--sc-event-color,#4c6f94) 30%,#fff)}.sc-day-count-mode.sc-today .sc-event-count{background:var(--sc-event-color,#4c6f94);color:#fff}@media (prefers-color-scheme:light){.sc-day-count-mode.sc-today .sc-event-count{background:var(--sc-event-color,#4c6f94);color:#fff}}[data-theme=light] .sc-day-count-mode.sc-today .sc-event-count{background:var(--sc-event-color,#4c6f94);color:#fff}@media (prefers-color-scheme:dark){.sc-day-count-mode.sc-today .sc-event-count{background:var(--sc-event-color,#4c6f94);color:#fff}}[data-theme=dark] .sc-day-count-mode.sc-today .sc-event-count{background:var(--sc-event-color,#4c6f94);color:#fff}.sc-day-count-mode.sc-other-month .sc-event-count{background:var(--sc-bg-secondary);color:var(--sc-text-muted)}.sc-day-events{flex:1;display:flex;flex-direction:column;gap:2px;overflow:hidden;padding:4px}.sc-month-view.sc-fullday .sc-day-number{flex-shrink:0;margin-bottom:4px;padding:4px 6px 0 6px}.sc-event-day,.sc-event-month,.sc-event-time,.sc-event-week{background:var(--sc-event-color,#4c6f94);color:#fff;border-radius:3px;line-height:1.2;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;box-sizing:border-box}.sc-month-view .sc-event-month{padding:2px 6px;font-size:.75rem;height:18px;margin:1px 4px;min-width:0;display:block}.sc-month-view.sc-fullday .sc-event-month{position:relative;padding:2px 6px;border-radius:3px;min-height:18px;display:block;box-sizing:border-box;overflow:visible}.sc-month-view.sc-fullday .sc-event-text{font-size:.75rem;line-height:1.2;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;position:absolute;display:block;width:100%}.sc-month-view .sc-event-single{border-radius:3px}.sc-month-view .sc-event-start{border-radius:3px 0 0 3px}.sc-month-view .sc-event-middle{border-radius:0}.sc-month-view .sc-event-end{border-radius:0 3px 3px 0}.sc-month-view .sc-event-month.sc-event-start{border-radius:3px 0 0 3px}.sc-month-view .sc-event-month.sc-event-middle{border-radius:0}.sc-month-view .sc-event-month.sc-event-end{border-radius:0 3px 3px 0}.sc-month-view.sc-fullday .sc-event-month.sc-event-start{position:relative}.sc-month-view.sc-fullday .sc-event-month.sc-event-start::after{content:'';position:absolute;right:-2px;top:0;bottom:0;width:2px;background:inherit;z-index:10}.sc-month-view.sc-fullday .sc-event-month.sc-event-middle{position:relative}.sc-month-view.sc-fullday .sc-event-month.sc-event-middle::after,.sc-month-view.sc-fullday .sc-event-month.sc-event-middle::before{content:'';position:absolute;top:0;bottom:0;width:2px;background:inherit;z-index:10}.sc-month-view.sc-fullday .sc-event-month.sc-event-middle::before{left:-2px}.sc-month-view.sc-fullday .sc-event-month.sc-event-middle::after{right:-2px}.sc-month-view.sc-fullday .sc-event-month.sc-event-end{position:relative}.sc-month-view.sc-fullday .sc-event-month.sc-event-end::before{content:'';position:absolute;left:-2px;top:0;bottom:0;width:2px;background:inherit;z-index:10}.sc-month-view.sc-fullday .sc-event-month.sc-event-single{margin:1px 4px!important;border-radius:3px!important;z-index:1;position:relative}.sc-month-view.sc-fullday .sc-event-month.sc-event-start{margin:1px 0 1px 4px!important;border-radius:3px 0 0 3px!important;z-index:2;position:relative}.sc-month-view.sc-fullday .sc-event-month.sc-event-middle{margin:1px 0!important;border-radius:0!important;z-index:2;position:relative}.sc-month-view.sc-fullday .sc-event-month.sc-event-end{margin:1px 4px 1px 0!important;border-radius:0 3px 3px 0!important;z-index:2;position:relative}.sc-event-placeholder{opacity:0!important}.sc-month-view.sc-fullday .sc-event-placeholder{min-height:18px;padding:2px 6px;margin:1px 4px;display:block;box-sizing:border-box}.sc-week-view.sc-fullday .sc-event-placeholder{min-height:18px;padding:2px 6px;margin:1px 4px;display:block;box-sizing:border-box;line-height:.9em}.sc-week-view{height:100%;overflow-y:auto;overflow-x:hidden;display:grid;grid-template-columns:80px repeat(7,1fr);align-content:start}.sc-day-header,.sc-time-column-header{position:sticky;top:0;z-index:20;display:flex;flex-direction:column;justify-content:center;text-align:center;padding:12px 8px;background:var(--sc-bg-secondary);height:60px;box-sizing:border-box}.sc-week-view .sc-day-header.sc-today{background:var(--sc-today-bg)}.sc-day-name{font-size:.75rem;color:var(--sc-text-secondary);font-weight:500;margin-bottom:2px}.sc-day-header .sc-day-number{font-size:1.25rem;font-weight:600;color:var(--sc-text-primary)}.sc-week-view .sc-day-header.sc-today .sc-day-number{color:var(--sc-today-text);font-weight:600}.sc-day-column,.sc-time-slot{height:60px;position:relative;box-sizing:border-box}.sc-time-slot{background:var(--sc-bg-secondary);padding:4px 8px 8px 8px;font-size:.75rem;color:var(--sc-text-secondary);text-align:center;display:flex;align-items:flex-start;justify-content:center}.sc-time-slot:first-child,.sc-time-slot:last-child{background:var(--sc-bg-tertiary);color:#495057;font-weight:500;font-style:italic}.sc-day-column{cursor:pointer;background:var(--sc-bg-primary)}.sc-week-view .sc-day-column.sc-today{background:var(--sc-today-bg)}.sc-day-column:hover{background:var(--sc-hover-bg)}.sc-half-hour-line{position:absolute;top:50%;left:0;right:0;height:0;pointer-events:none;z-index:1}.sc-week-view .sc-event-time{position:absolute;top:4px;left:4px;right:4px;padding:2px 6px;font-size:.75rem;line-height:1.2;border-radius:3px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;z-index:5;min-height:18px;display:block;box-sizing:border-box;padding-top:3px;padding-bottom:1px}.sc-week-view .sc-event-single{border-radius:3px}.sc-week-view .sc-event-start{border-radius:3px 0 0 3px}.sc-week-view .sc-event-middle{border-radius:0}.sc-week-view .sc-event-end{border-radius:0 3px 3px 0}.sc-week-view.sc-fullday .sc-event{width:100%;margin:2px 4px;padding:2px 6px;font-size:.75rem;height:18px;min-width:0}.sc-week-view.sc-fullday .sc-event-start{margin-right:0}.sc-week-view.sc-fullday .sc-event-middle{margin-left:0;margin-right:0}.sc-week-view.sc-fullday .sc-event-end{margin-left:0}.sc-week-view.sc-fullday{height:auto;display:flex;flex-direction:column}.sc-week-view.sc-fullday .sc-weekdays{display:grid;grid-template-columns:repeat(7,1fr);background:var(--sc-bg-secondary)}.sc-week-view.sc-fullday .sc-weekday{padding:12px 8px;text-align:center;font-weight:600;font-size:.875rem;color:var(--sc-text-secondary)}.sc-borders-horizontal .sc-week-view.sc-fullday .sc-weekdays{border-bottom:1px solid var(--sc-border-color)}.sc-borders-both .sc-week-view.sc-fullday .sc-weekday,.sc-borders-vertical .sc-week-view.sc-fullday .sc-weekday{border-right:1px solid var(--sc-border-color)}.sc-borders-both .sc-week-view.sc-fullday .sc-weekday:nth-child(7n),.sc-borders-vertical .sc-week-view.sc-fullday .sc-weekday:nth-child(7n){border-right:1px solid var(--sc-border-color)}.sc-borders-both .sc-week-view.sc-fullday .sc-weekday:first-child,.sc-borders-vertical .sc-week-view.sc-fullday .sc-weekday:first-child{border-left:1px solid var(--sc-border-color)}.sc-week-view.sc-fullday .sc-days-grid{display:grid;grid-template-columns:repeat(7,1fr);grid-template-rows:1fr;flex:1;min-height:auto;overflow:visible}.sc-week-view.sc-fullday .sc-day{display:flex;flex-direction:column;min-height:80px;position:relative;cursor:pointer;transition:background-color .2s ease;overflow:visible}.sc-week-view.sc-fullday .sc-day:hover{background:var(--sc-hover-bg)}.sc-week-view.sc-fullday .sc-day-number{text-align:center;font-weight:500;margin-bottom:4px;font-size:.875rem;line-height:1.2;flex-shrink:0;padding:4px 6px 0 6px}.sc-week-view.sc-fullday .sc-day.sc-today{background:var(--sc-today-bg)}.sc-week-view.sc-fullday .sc-day.sc-today .sc-day-number{color:var(--sc-today-text);font-weight:600}.sc-week-view.sc-fullday .sc-event-week{position:relative;border-radius:3px;min-height:18px;padding:2px 6px;display:block;box-sizing:border-box;overflow:visible}.sc-week-view.sc-fullday .sc-event-text{font-size:.75rem;line-height:1.2;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;position:absolute;display:block;width:100%}.sc-week-view.sc-fullday .sc-event-week.sc-event-single{margin:1px 4px!important;border-radius:3px!important;z-index:1;position:relative}.sc-week-view.sc-fullday .sc-event-week.sc-event-start{margin:1px 0 1px 4px!important;border-radius:3px 0 0 3px!important;z-index:2;position:relative}.sc-week-view.sc-fullday .sc-event-week.sc-event-start::after{content:'';position:absolute;right:-2px;top:0;bottom:0;width:2px;background:inherit;z-index:10}.sc-week-view.sc-fullday .sc-event-week.sc-event-middle{margin:1px 0!important;border-radius:0!important;z-index:2;position:relative}.sc-week-view.sc-fullday .sc-event-week.sc-event-middle::after,.sc-week-view.sc-fullday .sc-event-week.sc-event-middle::before{content:'';position:absolute;top:0;bottom:0;width:2px;background:inherit;z-index:10}.sc-week-view.sc-fullday .sc-event-week.sc-event-middle::before{left:-2px}.sc-week-view.sc-fullday .sc-event-week.sc-event-middle::after{right:-2px}.sc-week-view.sc-fullday .sc-event-week.sc-event-end{margin:1px 4px 1px 0!important;border-radius:0 3px 3px 0!important;z-index:2;position:relative}.sc-week-view.sc-fullday .sc-event-week.sc-event-end::before{content:'';position:absolute;left:-2px;top:0;bottom:0;width:2px;background:inherit;z-index:10}.sc-day-view{height:100%;overflow-y:auto;overflow-x:hidden;max-height:calc(100vh - 200px);display:grid;grid-template-columns:80px 1fr;align-content:start}.sc-day-view.sc-fullday{display:flex;flex-direction:column;padding:0;grid-template-columns:none}.sc-day-view.sc-fullday .sc-day-header{position:sticky;top:0;z-index:10;background:var(--sc-bg-secondary);border-bottom:1px solid var(--sc-border-color);padding:16px;text-align:center;flex-shrink:0}.sc-day-view.sc-fullday .sc-day-name{font-size:.875rem;color:var(--sc-text-secondary);font-weight:500;margin-bottom:4px}.sc-day-view.sc-fullday .sc-day-number{font-size:1.5rem;font-weight:600;color:var(--sc-text-primary)}.sc-day-view.sc-fullday .sc-day-header.sc-today .sc-day-number{color:var(--sc-today-text)}.sc-day-view.sc-fullday .sc-day-events{flex:1;padding:16px;display:flex;flex-direction:column;gap:8px;overflow-y:auto;cursor:pointer}.sc-day-view .sc-event-time{position:absolute;top:4px;left:4px;right:4px;padding:2px 6px;font-size:.75rem;line-height:1.2;border-radius:3px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;z-index:5;min-height:18px;display:block;box-sizing:border-box;padding-top:3px;padding-bottom:1px}.sc-day-view .sc-event{width:100%;margin:2px 4px;padding:2px 6px;font-size:.75rem;height:18px;min-width:0}.sc-day-view .sc-event-single{border-radius:3px}.sc-day-view .sc-event-start{border-radius:3px 0 0 3px}.sc-day-view .sc-event-middle{border-radius:0}.sc-day-view .sc-event-end{border-radius:0 3px 3px 0}.sc-day-view.sc-fullday .sc-event-start{margin-right:0}.sc-day-view.sc-fullday .sc-event-middle{margin-left:0;margin-right:0}.sc-day-view.sc-fullday .sc-event-end{margin-left:0}.sc-day-view.sc-fullday .sc-event-day{position:relative;padding:2px 6px;border-radius:3px;margin:1px 4px;min-height:18px;box-sizing:border-box;display:block}.sc-day-view.sc-fullday .sc-event-text{font-size:.75rem;line-height:1.2;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;position:absolute;display:block;width:100%}@media (max-width:768px){.sc-header{grid-template-columns:1fr;grid-template-rows:auto auto auto;gap:8px;justify-items:center}.sc-view-switcher{grid-row:1;justify-self:center}.sc-title-container{grid-row:2;justify-self:center}.sc-title{font-size:1.125rem}.sc-nav{grid-row:3;justify-self:center}.sc-days-grid{min-height:360px;grid-template-rows:repeat(6,minmax(60px,1fr))}.sc-day-number{font-size:.75rem}.sc-day-count-mode .sc-day-number{font-size:1rem}.sc-event-count{font-size:.625rem;padding:1px 4px;min-width:14px}.sc-event-month{font-size:.6rem;padding:1px 3px;height:14px}.sc-day-view .sc-event-time,.sc-week-view .sc-event-time{top:3px;left:3px;right:3px;padding:1px 4px;padding-top:2px;padding-bottom:1px;font-size:.65rem;min-height:16px}.sc-weekday{padding:8px 4px;font-size:.75rem}.sc-week-header{min-height:50px}.sc-week-view{grid-template-columns:60px repeat(7,1fr)}.sc-week-view.sc-fullday{grid-template-columns:repeat(7,1fr)}.sc-day-header{padding:8px 4px}.sc-day-name{font-size:.625rem}.sc-day-header .sc-day-number{font-size:1rem}.sc-day-header.sc-today .sc-day-number{color:var(--sc-today-text);font-weight:600}.sc-time-slot{height:40px;padding:4px;font-size:.625rem}.sc-hour-slot{height:40px}.sc-day-view{grid-template-columns:60px 1fr;max-height:calc(100vh - 160px)}.sc-week-view{max-height:calc(100vh - 160px)}}@media (max-width:480px){.sc-calendar{border-radius:0;box-shadow:none;border:1px solid #e9ecef}.sc-header{padding:12px}.sc-btn{padding:6px 8px;font-size:.75rem;min-width:30px}.sc-title{font-size:1rem}.sc-day{min-height:50px;padding:2px}.sc-days-grid{min-height:300px;grid-template-rows:repeat(6,minmax(50px,1fr))}.sc-day-count-mode .sc-day-number{font-size:.875rem}.sc-event-count{font-size:.5rem;padding:1px 3px;min-width:12px}.sc-weekday{padding:6px 2px;font-size:.625rem}.sc-week-header{min-height:40px}.sc-week-view{grid-template-columns:50px repeat(7,1fr);max-height:calc(100vh - 140px)}.sc-week-view.sc-fullday{grid-template-columns:repeat(7,1fr)}.sc-time-slot{height:30px;padding:2px;font-size:.5rem}.sc-hour-slot{height:30px}.sc-day-view{grid-template-columns:50px 1fr;max-height:calc(100vh - 140px)}.sc-day-view .sc-event-time,.sc-week-view .sc-event-time{top:2px;left:2px;right:2px;padding:1px 3px;padding-top:2px;padding-bottom:0;font-size:.5rem;min-height:14px}}.sc-borders-both .sc-month-view .sc-days-grid{border-left:1px solid var(--sc-border-color);border-bottom:1px solid var(--sc-border-color)}.sc-borders-vertical .sc-month-view .sc-days-grid{border-left:1px solid var(--sc-border-color);border-bottom:none}.sc-borders-horizontal .sc-month-view .sc-days-grid{border-left:none;border-bottom:1px solid var(--sc-border-color)}.sc-borders-none .sc-month-view .sc-days-grid{border-left:none;border-bottom:none}.sc-borders-both .sc-day{border-right:1px solid var(--sc-border-color);border-top:1px solid var(--sc-border-color)}.sc-borders-vertical .sc-day{border-right:1px solid var(--sc-border-color);border-top:none}.sc-borders-horizontal .sc-day{border-right:none;border-top:1px solid var(--sc-border-color)}.sc-borders-none .sc-day{border-right:none;border-top:none}.sc-borders-both .sc-day-column,.sc-borders-both .sc-time-slot{border-right:1px solid var(--sc-border-color);border-bottom:1px solid var(--sc-border-color)}.sc-borders-vertical .sc-day-column,.sc-borders-vertical .sc-time-slot{border-right:1px solid var(--sc-border-color);border-bottom:none}.sc-borders-horizontal .sc-day-column,.sc-borders-horizontal .sc-time-slot{border-right:none;border-bottom:1px solid var(--sc-border-color)}.sc-borders-none .sc-day-column,.sc-borders-none .sc-time-slot{border-right:none;border-bottom:none}.sc-week-view:not(.sc-fullday) .sc-time-slot{border-bottom:1px solid var(--sc-border-color)!important}.sc-week-view:not(.sc-fullday) .sc-day-column{border-bottom:1px solid var(--sc-border-color)!important}.sc-borders-both .sc-week-view.sc-fullday .sc-day{border-right:1px solid var(--sc-border-color);border-bottom:1px solid var(--sc-border-color)}.sc-borders-both .sc-week-view.sc-fullday .sc-day:first-child{border-left:1px solid var(--sc-border-color)}.sc-borders-vertical .sc-week-view.sc-fullday .sc-day{border-right:1px solid var(--sc-border-color);border-bottom:none}.sc-borders-horizontal .sc-week-view.sc-fullday .sc-day{border-right:none;border-bottom:1px solid var(--sc-border-color)}.sc-borders-none .sc-week-view.sc-fullday .sc-day{border-right:none;border-bottom:none}.sc-borders-both .sc-day-header,.sc-borders-both .sc-time-column-header{border-right:1px solid var(--sc-border-color);border-bottom:1px solid var(--sc-border-color)}.sc-borders-both .sc-day-header:first-child,.sc-borders-both .sc-time-column-header:first-child,.sc-borders-vertical .sc-day-header:first-child,.sc-borders-vertical .sc-time-column-header:first-child{border-left:1px solid var(--sc-border-color)}.sc-borders-vertical .sc-day-header,.sc-borders-vertical .sc-time-column-header{border-right:1px solid var(--sc-border-color);border-bottom:none}.sc-borders-horizontal .sc-day-header,.sc-borders-horizontal .sc-time-column-header{border-right:none;border-bottom:1px solid var(--sc-border-color)}.sc-borders-none .sc-day-header,.sc-borders-none .sc-time-column-header{border-right:none;border-bottom:none}.sc-borders-both .sc-week-view:not(.sc-fullday){border-left:1px solid var(--sc-border-color);border-right:1px solid var(--sc-border-color)}.sc-borders-vertical .sc-week-view:not(.sc-fullday){border-left:1px solid var(--sc-border-color);border-right:1px solid var(--sc-border-color)}.sc-borders-both .sc-day-header:nth-child(8n),.sc-borders-vertical .sc-day-header:nth-child(8n){border-right:none}.sc-borders-both .sc-half-hour-line,.sc-borders-horizontal .sc-half-hour-line{border-top:1px dotted var(--sc-border-color)}.sc-borders-none .sc-half-hour-line,.sc-borders-vertical .sc-half-hour-line{border-top:none}.sc-week-view:not(.sc-fullday) .sc-half-hour-line{border-top:1px dotted var(--sc-border-color)!important}.sc-day-view:not(.sc-fullday) .sc-time-slot{border-bottom:1px solid var(--sc-border-color)!important}.sc-borders-both .sc-day-view:not(.sc-fullday) .sc-time-slot,.sc-borders-vertical .sc-day-view:not(.sc-fullday) .sc-time-slot{border-left:1px solid var(--sc-border-color)!important}.sc-day-view:not(.sc-fullday) .sc-day-column{border-bottom:1px solid var(--sc-border-color)!important}.sc-day-view:not(.sc-fullday) .sc-half-hour-line{border-top:1px dotted var(--sc-border-color)!important}.sc-borders-both .sc-month-view .sc-days-grid,.sc-borders-vertical .sc-month-view .sc-days-grid{border-right:1px solid var(--sc-border-color)}.sc-borders-both .sc-day:nth-child(7n),.sc-borders-vertical .sc-day:nth-child(7n){border-right:none}:root{--sc-event-border-color:#6c757d}.sc-event-borders .sc-event-day:not(.sc-event-placeholder),.sc-event-borders .sc-event-month:not(.sc-event-placeholder),.sc-event-borders .sc-event-time:not(.sc-event-placeholder),.sc-event-borders .sc-event-week:not(.sc-event-placeholder){border:1px solid var(--sc-event-border-color)}.sc-event-borders .sc-event-single:not(.sc-event-placeholder){border:1px solid var(--sc-event-border-color)}.sc-event-borders .sc-event-start:not(.sc-event-placeholder){border-left:1px solid var(--sc-event-border-color);border-top:1px solid var(--sc-event-border-color);border-bottom:1px solid var(--sc-event-border-color)}.sc-event-borders .sc-event-middle:not(.sc-event-placeholder){border-top:1px solid var(--sc-event-border-color);border-bottom:1px solid var(--sc-event-border-color)}.sc-event-borders .sc-event-end:not(.sc-event-placeholder){border-right:1px solid var(--sc-event-border-color);border-top:1px solid var(--sc-event-border-color);border-bottom:1px solid var(--sc-event-border-color)}.sc-week-view .sc-event-week{border-right:none}.sc-event-no-borders .sc-event-day,.sc-event-no-borders .sc-event-month,.sc-event-no-borders .sc-event-time,.sc-event-no-borders .sc-event-week{border:none!important}@media print{.sc-calendar{box-shadow:none;border:1px solid #000}.sc-header{background:#fff!important;-webkit-print-color-adjust:exact;print-color-adjust:exact}.sc-btn{display:none}.sc-view-switcher{display:none}}
10
+ .sc-calendar{--sc-bg-primary:#ffffff;--sc-bg-secondary:#f8f9fa;--sc-bg-tertiary:#e9ecef;--sc-text-primary:#212529;--sc-text-secondary:#6c757d;--sc-text-muted:#adb5bd;--sc-border-color:#e9ecef;--sc-border-light:#dee2e6;--sc-accent-color:#4c6f94;--sc-accent-hover:#0056b3;--sc-today-bg:#e3f2fd;--sc-today-text:#dc3545;--sc-hover-bg:#f8f9fa;--sc-shadow:none;--sc-shadow-enabled:0 2px 10px rgba(0, 0, 0, 0.1);font-family:inherit;background:var(--sc-bg-primary);color:var(--sc-text-primary);box-shadow:var(--sc-shadow);overflow:hidden;width:100%;max-width:100%;display:flex;flex-direction:column}.sc-calendar.sc-shadow{--sc-shadow:var(--sc-shadow-enabled)}@media (prefers-color-scheme:dark){.sc-calendar{--sc-bg-primary:#1a1a1a;--sc-bg-secondary:#2d2d2d;--sc-bg-tertiary:#3a3a3a;--sc-text-primary:#ffffff;--sc-text-secondary:#b0b0b0;--sc-text-muted:#888888;--sc-border-color:#444444;--sc-border-light:#555555;--sc-accent-color:#4a90e2;--sc-accent-hover:#357abd;--sc-today-bg:#1a2f4a;--sc-today-text:#ff6b6b;--sc-hover-bg:#2d2d2d;--sc-shadow-enabled:0 2px 10px rgba(0, 0, 0, 0.5)}}[data-theme=dark] .sc-calendar{--sc-bg-primary:#1a1a1a;--sc-bg-secondary:#2d2d2d;--sc-bg-tertiary:#3a3a3a;--sc-text-primary:#ffffff;--sc-text-secondary:#b0b0b0;--sc-text-muted:#888888;--sc-border-color:#444444;--sc-border-light:#555555;--sc-accent-color:#4a90e2;--sc-accent-hover:#357abd;--sc-today-bg:#1a2f4a;--sc-today-text:#ff6b6b;--sc-hover-bg:#2d2d2d;--sc-shadow-enabled:0 2px 10px rgba(0, 0, 0, 0.5)}.dark .sc-calendar,body.dark .sc-calendar,html.dark .sc-calendar{--sc-bg-primary:#1a1a1a;--sc-bg-secondary:#2d2d2d;--sc-bg-tertiary:#3a3a3a;--sc-text-primary:#ffffff;--sc-text-secondary:#b0b0b0;--sc-text-muted:#888888;--sc-border-color:#444444;--sc-border-light:#555555;--sc-accent-color:#4a90e2;--sc-accent-hover:#357abd;--sc-today-bg:#1a2f4a;--sc-today-text:#ff6b6b;--sc-hover-bg:#2d2d2d;--sc-shadow-enabled:0 2px 10px rgba(0, 0, 0, 0.5)}[data-theme=light] .sc-calendar{--sc-bg-primary:#ffffff;--sc-bg-secondary:#f8f9fa;--sc-bg-tertiary:#e9ecef;--sc-text-primary:#212529;--sc-text-secondary:#6c757d;--sc-text-muted:#adb5bd;--sc-border-color:#e9ecef;--sc-border-light:#dee2e6;--sc-accent-color:#4c6f94;--sc-accent-hover:#0056b3;--sc-today-bg:#e3f2fd;--sc-today-text:#dc3545;--sc-hover-bg:#f8f9fa;--sc-shadow-enabled:0 2px 10px rgba(0, 0, 0, 0.1)}.sc-calendar{background:var(--calendar-bg,var(--sc-bg-primary));color:var(--calendar-text,var(--sc-text-primary));height:100%}.sc-header{background:0 0;padding:16px;display:grid;grid-template-columns:1fr auto 1fr;align-items:center;gap:12px}.sc-borders-both .sc-header,.sc-borders-horizontal .sc-header{border-bottom:1px solid var(--sc-border-color)}.sc-nav{display:flex;align-items:center;gap:8px;justify-self:start}.sc-title-container{position:relative;justify-self:center}.sc-title{margin:0;font-size:1.25rem;font-weight:600;color:var(--sc-text-primary);white-space:nowrap;text-align:center;user-select:none}.sc-title-month,.sc-title-year{cursor:pointer;padding:2px 4px;border-radius:3px;transition:background-color .2s ease}.sc-title-month:hover,.sc-title-year:hover{color:var(--sc-event-color,#4c6f94)}.sc-month-dropdown{position:absolute;top:100%;left:50%;transform:translateX(-50%);background:var(--sc-bg-primary);border:1px solid var(--sc-border-color);border-radius:4px;z-index:1000;min-width:120px;box-shadow:var(--sc-shadow-enabled);max-height:240px;overflow-y:auto}.sc-month-option{padding:4px 8px;font-size:.75rem;color:var(--sc-text-primary);cursor:pointer;transition:background-color .2s ease}.sc-month-option:hover{background:var(--sc-bg-tertiary)}.sc-month-option.sc-current{background:var(--sc-event-color,#4c6f94);color:#fff}.sc-month-option.sc-current:hover{background:color-mix(in srgb,var(--sc-event-color,#4c6f94) 85%,#000)}.sc-year-dropdown{position:absolute;top:100%;right:-20px;background:var(--sc-bg-primary);border:1px solid var(--sc-border-color);border-radius:4px;z-index:1000;min-width:100px;box-shadow:var(--sc-shadow-enabled);max-height:240px;overflow-y:auto}.sc-year-option{padding:4px 8px;font-size:.75rem;color:var(--sc-text-primary);cursor:pointer;transition:background-color .2s ease;text-align:center}.sc-year-option:hover{background:var(--sc-bg-tertiary)}.sc-year-option.sc-current{background:var(--sc-event-color,#4c6f94);color:#fff}.sc-year-option.sc-current:hover{background:color-mix(in srgb,var(--sc-event-color,#4c6f94) 85%,#000)}.sc-btn{background:0 0;border:1px solid var(--sc-border-light);border-radius:4px;padding:8px 12px;cursor:pointer;color:var(--sc-text-primary);font-size:.875rem;font-weight:500;transition:all .2s ease;min-width:36px;display:flex;align-items:center;justify-content:center}.sc-nav .sc-btn{background:0 0;border:none;font-size:1.2rem;padding:8px;min-width:32px;color:var(--sc-text-primary);font-weight:700}.sc-btn:hover{background:var(--sc-bg-tertiary);border-color:#adb5bd}.sc-nav .sc-btn:hover{background:var(--sc-bg-tertiary);color:var(--sc-text-primary)}.sc-btn:active{background:#dee2e6}.sc-nav .sc-btn:active{background:var(--sc-bg-tertiary)}.sc-view-switcher{display:flex;gap:4px;justify-self:end}.sc-view-btn.sc-active{background:var(--sc-event-color,#4c6f94);color:#fff;border-color:var(--sc-event-color,#4c6f94)}.sc-view-btn.sc-active:hover{background:color-mix(in srgb,var(--sc-event-color,#4c6f94) 85%,#000);border-color:color-mix(in srgb,var(--sc-event-color,#4c6f94) 85%,#000)}.sc-content{flex:1;height:100%;display:flex;flex-direction:column;overflow:hidden}.sc-view-container{flex:1;overflow:hidden;min-height:0}.sc-month-view{height:100%;display:flex;flex-direction:column;min-height:0}.sc-weekdays{display:grid;grid-template-columns:repeat(7,1fr);background:var(--sc-bg-secondary)}.sc-weekday{padding:12px 8px;text-align:center;font-weight:600;font-size:.875rem;color:var(--sc-text-secondary)}.sc-borders-both .sc-month-view .sc-weekdays,.sc-borders-vertical .sc-month-view .sc-weekdays{border-left:1px solid var(--sc-border-color);border-right:1px solid var(--sc-border-color)}.sc-borders-both .sc-month-view .sc-weekday,.sc-borders-vertical .sc-month-view .sc-weekday{border-right:1px solid var(--sc-border-color)}.sc-borders-both .sc-month-view .sc-weekday:nth-child(7n),.sc-borders-vertical .sc-month-view .sc-weekday:nth-child(7n){border-right:none}.sc-days-grid{display:grid;grid-template-columns:repeat(7,1fr);grid-template-rows:repeat(6,1fr);flex:1;overflow:visible;min-height:0}.sc-month-view.sc-fullday .sc-days-grid{grid-template-rows:repeat(6,auto);min-height:auto}.sc-month-view.sc-fullday .sc-day{display:flex;flex-direction:column;position:relative}.sc-day{display:flex;flex-direction:column;min-height:0;cursor:pointer;transition:background-color .2s ease;overflow:visible}.sc-day-number{text-align:center;font-weight:500;margin-bottom:4px;font-size:.875rem;line-height:1.2}.sc-day.sc-other-month .sc-day-number{color:var(--sc-text-muted)}.sc-day.sc-today{background:var(--sc-today-bg)}.sc-day.sc-today .sc-day-number{color:var(--sc-today-text);font-weight:600}.sc-day-count-mode{display:flex;align-items:center;justify-content:center}.sc-day-content{text-align:center;display:flex;flex-direction:column;align-items:center;justify-content:center}.sc-day-count-mode .sc-day-number{font-size:1.125rem;font-weight:600;margin-bottom:4px}.sc-event-count{font-size:.7rem;font-weight:600;line-height:1.2;background:color-mix(in srgb,var(--sc-event-color,#4c6f94) 15%,var(--sc-bg-tertiary));color:color-mix(in srgb,var(--sc-event-color,#4c6f94) 80%,#000);padding:2px 6px;border-radius:10px;display:inline-block;margin-top:2px;text-align:center;box-sizing:border-box}@media (prefers-color-scheme:light){.sc-event-count{background:color-mix(in srgb,var(--sc-event-color,#4c6f94) 15%,var(--sc-bg-tertiary));color:color-mix(in srgb,var(--sc-event-color,#4c6f94) 80%,#000)}}[data-theme=light] .sc-event-count{background:color-mix(in srgb,var(--sc-event-color,#4c6f94) 15%,var(--sc-bg-tertiary));color:color-mix(in srgb,var(--sc-event-color,#4c6f94) 80%,#000)}@media (prefers-color-scheme:dark){.sc-event-count{background:color-mix(in srgb,var(--sc-event-color,#4c6f94) 20%,var(--sc-bg-tertiary));color:color-mix(in srgb,var(--sc-event-color,#4c6f94) 30%,#fff)}}.dark .sc-event-count,[data-theme=dark] .sc-event-count,body.dark .sc-event-count,html.dark .sc-event-count{background:color-mix(in srgb,var(--sc-event-color,#4c6f94) 20%,var(--sc-bg-tertiary));color:color-mix(in srgb,var(--sc-event-color,#4c6f94) 30%,#fff)}.sc-day-count-mode.sc-today .sc-event-count{background:var(--sc-event-color,#4c6f94);color:#fff}@media (prefers-color-scheme:light){.sc-day-count-mode.sc-today .sc-event-count{background:var(--sc-event-color,#4c6f94);color:#fff}}[data-theme=light] .sc-day-count-mode.sc-today .sc-event-count{background:var(--sc-event-color,#4c6f94);color:#fff}@media (prefers-color-scheme:dark){.sc-day-count-mode.sc-today .sc-event-count{background:var(--sc-event-color,#4c6f94);color:#fff}}.dark .sc-day-count-mode.sc-today .sc-event-count,[data-theme=dark] .sc-day-count-mode.sc-today .sc-event-count,body.dark .sc-day-count-mode.sc-today .sc-event-count,html.dark .sc-day-count-mode.sc-today .sc-event-count{background:var(--sc-event-color,#4c6f94);color:#fff}.sc-day-count-mode.sc-other-month .sc-event-count{background:var(--sc-bg-secondary);color:var(--sc-text-muted)}.sc-day-events{flex:1;display:flex;flex-direction:column;gap:2px;overflow:hidden;padding:4px}.sc-month-view.sc-fullday .sc-day-number{flex-shrink:0;margin-bottom:4px;padding:4px 6px 0 6px}.sc-event-day,.sc-event-month,.sc-event-time,.sc-event-week{background:var(--sc-event-color,#4c6f94);color:#fff;border-radius:3px;line-height:1.2;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;box-sizing:border-box}.sc-month-view .sc-event-month{padding:2px 6px;font-size:.75rem;height:18px;margin:1px 4px;min-width:0;max-width:100%;display:block;box-sizing:border-box}.sc-month-view.sc-fullday .sc-event-month{position:relative;padding:2px 6px;border-radius:3px;min-height:18px;display:block;box-sizing:border-box;overflow:visible}.sc-month-view.sc-fullday .sc-event-text{font-size:.75rem;line-height:1.2;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;position:absolute;display:block;width:calc(100% - 12px);max-width:calc(100% - 12px);box-sizing:border-box;left:6px;right:6px}.sc-month-view .sc-event-single{border-radius:3px}.sc-month-view .sc-event-start{border-radius:3px 0 0 3px}.sc-month-view .sc-event-middle{border-radius:0}.sc-month-view .sc-event-end{border-radius:0 3px 3px 0}.sc-month-view .sc-event-month.sc-event-start{border-radius:3px 0 0 3px}.sc-month-view .sc-event-month.sc-event-middle{border-radius:0}.sc-month-view .sc-event-month.sc-event-end{border-radius:0 3px 3px 0}.sc-month-view.sc-fullday .sc-event-month.sc-event-start{position:relative}.sc-month-view.sc-fullday .sc-event-month.sc-event-start::after{content:'';position:absolute;right:-2px;top:0;bottom:0;width:2px;background:inherit;z-index:10}.sc-month-view.sc-fullday .sc-event-month.sc-event-middle{position:relative}.sc-month-view.sc-fullday .sc-event-month.sc-event-middle::after,.sc-month-view.sc-fullday .sc-event-month.sc-event-middle::before{content:'';position:absolute;top:0;bottom:0;width:2px;background:inherit;z-index:10}.sc-month-view.sc-fullday .sc-event-month.sc-event-middle::before{left:-2px}.sc-month-view.sc-fullday .sc-event-month.sc-event-middle::after{right:-2px}.sc-month-view.sc-fullday .sc-event-month.sc-event-end{position:relative}.sc-month-view.sc-fullday .sc-event-month.sc-event-end::before{content:'';position:absolute;left:-2px;top:0;bottom:0;width:2px;background:inherit;z-index:10}.sc-month-view.sc-fullday .sc-event-month.sc-event-single{margin:1px 4px!important;border-radius:3px!important;z-index:1;position:relative}.sc-month-view.sc-fullday .sc-event-month.sc-event-start{margin:1px 0 1px 4px!important;border-radius:3px 0 0 3px!important;z-index:2;position:relative}.sc-month-view.sc-fullday .sc-event-month.sc-event-middle{margin:1px 0!important;border-radius:0!important;z-index:2;position:relative}.sc-month-view.sc-fullday .sc-event-month.sc-event-end{margin:1px 4px 1px 0!important;border-radius:0 3px 3px 0!important;z-index:2;position:relative}.sc-event-placeholder{opacity:0!important}.sc-month-view.sc-fullday .sc-event-placeholder{min-height:18px;padding:2px 6px;margin:1px 4px;display:block;box-sizing:border-box}.sc-week-view.sc-fullday .sc-event-placeholder{min-height:18px;padding:2px 6px;margin:1px 4px;display:block;box-sizing:border-box;line-height:.9em}.sc-week-view{height:100%;overflow-y:auto;overflow-x:hidden;display:grid;grid-template-columns:80px repeat(7,1fr);grid-template-rows:auto 1fr;align-content:stretch;min-height:0}.sc-day-header,.sc-time-column-header{position:sticky;top:0;z-index:20;display:flex;flex-direction:column;justify-content:center;text-align:center;padding:12px 8px;background:var(--sc-bg-secondary);height:60px;box-sizing:border-box}.sc-week-view .sc-day-header.sc-today{background:var(--sc-today-bg)}.sc-day-name{font-size:.75rem;color:var(--sc-text-secondary);font-weight:500;margin-bottom:2px}.sc-day-header .sc-day-number{font-size:1.25rem;font-weight:600;color:var(--sc-text-primary)}.sc-week-view .sc-day-header.sc-today .sc-day-number{color:var(--sc-today-text);font-weight:600}.sc-day-column,.sc-time-slot{height:60px;position:relative;box-sizing:border-box}.sc-time-slot{background:var(--sc-bg-secondary);padding:4px 8px 8px 8px;font-size:.75rem;color:var(--sc-text-secondary);text-align:center;display:flex;align-items:flex-start;justify-content:center}.sc-time-slot:first-child,.sc-time-slot:last-child{background:var(--sc-bg-tertiary);color:#495057;font-weight:500;font-style:italic}.sc-day-column{cursor:pointer;background:var(--sc-bg-primary)}.sc-week-view .sc-day-column.sc-today{background:var(--sc-today-bg)}.sc-half-hour-line{position:absolute;top:50%;left:0;right:0;height:0;pointer-events:none;z-index:1}.sc-week-view .sc-event-time{position:absolute;top:4px;left:4px;right:4px;padding:2px 6px;font-size:.75rem;line-height:1.2;border-radius:3px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;z-index:5;min-height:18px;display:block;box-sizing:border-box;padding-top:3px;padding-bottom:1px}.sc-week-view .sc-event-single{border-radius:3px}.sc-week-view .sc-event-start{border-radius:3px 0 0 3px}.sc-week-view .sc-event-middle{border-radius:0}.sc-week-view .sc-event-end{border-radius:0 3px 3px 0}.sc-week-view.sc-fullday .sc-event{width:100%;margin:2px 4px;padding:2px 6px;font-size:.75rem;height:18px;min-width:0}.sc-week-view.sc-fullday .sc-event-start{margin-right:0}.sc-week-view.sc-fullday .sc-event-middle{margin-left:0;margin-right:0}.sc-week-view.sc-fullday .sc-event-end{margin-left:0}.sc-week-view.sc-fullday{height:auto;display:flex;flex-direction:column}.sc-week-view.sc-fullday .sc-weekdays{display:grid;grid-template-columns:repeat(7,1fr);background:var(--sc-bg-secondary)}.sc-week-view.sc-fullday .sc-weekday{padding:12px 8px;text-align:center;font-weight:600;font-size:.875rem;color:var(--sc-text-secondary)}.sc-borders-horizontal .sc-week-view.sc-fullday .sc-weekdays{border-bottom:1px solid var(--sc-border-color)}.sc-borders-both .sc-week-view.sc-fullday .sc-weekday,.sc-borders-vertical .sc-week-view.sc-fullday .sc-weekday{border-right:1px solid var(--sc-border-color)}.sc-borders-both .sc-week-view.sc-fullday .sc-weekday:nth-child(7n),.sc-borders-vertical .sc-week-view.sc-fullday .sc-weekday:nth-child(7n){border-right:1px solid var(--sc-border-color)}.sc-borders-both .sc-week-view.sc-fullday .sc-weekday:first-child,.sc-borders-vertical .sc-week-view.sc-fullday .sc-weekday:first-child{border-left:1px solid var(--sc-border-color)}.sc-week-view.sc-fullday .sc-days-grid{display:grid;grid-template-columns:repeat(7,1fr);grid-template-rows:1fr;flex:1;min-height:auto;overflow:visible}.sc-week-view.sc-fullday .sc-day{display:flex;flex-direction:column;min-height:80px;position:relative;cursor:pointer;transition:background-color .2s ease;overflow:visible}.sc-week-view.sc-fullday .sc-day-number{text-align:center;font-weight:500;margin-bottom:4px;font-size:.875rem;line-height:1.2;flex-shrink:0;padding:4px 6px 0 6px}.sc-week-view.sc-fullday .sc-day.sc-today{background:var(--sc-today-bg)}.sc-week-view.sc-fullday .sc-day.sc-today .sc-day-number{color:var(--sc-today-text);font-weight:600}.sc-week-view.sc-fullday .sc-event-week{position:relative;border-radius:3px;min-height:18px;padding:2px 6px;display:block;box-sizing:border-box;overflow:visible}.sc-week-view.sc-fullday .sc-event-text{font-size:.75rem;line-height:1.2;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;position:absolute;display:block;width:calc(100% - 12px);max-width:calc(100% - 12px);box-sizing:border-box;left:6px;right:6px}.sc-week-view.sc-fullday .sc-event-week.sc-event-single{margin:1px 4px!important;border-radius:3px!important;z-index:1;position:relative}.sc-week-view.sc-fullday .sc-event-week.sc-event-start{margin:1px 0 1px 4px!important;border-radius:3px 0 0 3px!important;z-index:2;position:relative}.sc-week-view.sc-fullday .sc-event-week.sc-event-start::after{content:'';position:absolute;right:-2px;top:0;bottom:0;width:2px;background:inherit;z-index:10}.sc-week-view.sc-fullday .sc-event-week.sc-event-middle{margin:1px 0!important;border-radius:0!important;z-index:2;position:relative}.sc-week-view.sc-fullday .sc-event-week.sc-event-middle::after,.sc-week-view.sc-fullday .sc-event-week.sc-event-middle::before{content:'';position:absolute;top:0;bottom:0;width:2px;background:inherit;z-index:10}.sc-week-view.sc-fullday .sc-event-week.sc-event-middle::before{left:-2px}.sc-week-view.sc-fullday .sc-event-week.sc-event-middle::after{right:-2px}.sc-week-view.sc-fullday .sc-event-week.sc-event-end{margin:1px 4px 1px 0!important;border-radius:0 3px 3px 0!important;z-index:2;position:relative}.sc-week-view.sc-fullday .sc-event-week.sc-event-end::before{content:'';position:absolute;left:-2px;top:0;bottom:0;width:2px;background:inherit;z-index:10}.sc-day-view{height:100%;overflow-y:auto;overflow-x:hidden;max-height:calc(100vh - 200px);display:grid;grid-template-columns:80px 1fr;grid-template-rows:auto 1fr;align-content:stretch;min-height:0}.sc-day-view.sc-fullday{display:flex;flex-direction:column;padding:0;grid-template-columns:none}.sc-day-view.sc-fullday .sc-day-header{position:sticky;top:0;z-index:10;background:var(--sc-bg-secondary);border-bottom:1px solid var(--sc-border-color);padding:16px;text-align:center;flex-shrink:0}.sc-day-view.sc-fullday .sc-day-name{font-size:.875rem;color:var(--sc-text-secondary);font-weight:500;margin-bottom:4px}.sc-day-view.sc-fullday .sc-day-number{font-size:1.5rem;font-weight:600;color:var(--sc-text-primary)}.sc-day-view.sc-fullday .sc-day-header.sc-today .sc-day-number{color:var(--sc-today-text)}.sc-day-view.sc-fullday .sc-day-events{flex:1;padding:16px;display:flex;flex-direction:column;gap:8px;overflow-y:auto;cursor:pointer}.sc-day-view .sc-event-time{position:absolute;top:4px;left:4px;right:4px;padding:2px 6px;font-size:.75rem;line-height:1.2;border-radius:3px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;z-index:5;min-height:18px;display:block;box-sizing:border-box;padding-top:3px;padding-bottom:1px}.sc-day-view .sc-event{width:100%;margin:2px 4px;padding:2px 6px;font-size:.75rem;height:18px;min-width:0}.sc-day-view .sc-event-single{border-radius:3px}.sc-day-view .sc-event-start{border-radius:3px 0 0 3px}.sc-day-view .sc-event-middle{border-radius:0}.sc-day-view .sc-event-end{border-radius:0 3px 3px 0}.sc-day-view.sc-fullday .sc-event-start{margin-right:0}.sc-day-view.sc-fullday .sc-event-middle{margin-left:0;margin-right:0}.sc-day-view.sc-fullday .sc-event-end{margin-left:0}.sc-day-view.sc-fullday .sc-event-day{position:relative;padding:2px 6px;border-radius:3px;margin:1px 4px;min-height:18px;box-sizing:border-box;display:block}.sc-day-view.sc-fullday .sc-event-text{font-size:.75rem;line-height:1.2;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;position:absolute;display:block;width:calc(100% - 12px);max-width:calc(100% - 12px);box-sizing:border-box;left:6px;right:6px}@media (max-width:768px){.sc-header{grid-template-columns:1fr;grid-template-rows:auto auto auto;gap:8px;justify-items:center}.sc-view-switcher{grid-row:1;justify-self:center}.sc-title-container{grid-row:2;justify-self:center}.sc-title{font-size:1.125rem}.sc-nav{grid-row:3;justify-self:center}.sc-days-grid{min-height:0;grid-template-rows:repeat(6,1fr)}.sc-day-number{font-size:.75rem}.sc-day-count-mode .sc-day-number{font-size:1rem}.sc-event-count{font-size:.625rem;padding:1px 4px;min-width:14px}.sc-event-month{font-size:.6rem;padding:1px 3px;height:14px;max-width:calc(100% - 8px);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.sc-day-view .sc-event-time,.sc-week-view .sc-event-time{top:3px;left:3px;right:3px;padding:1px 4px;padding-top:2px;padding-bottom:1px;font-size:.65rem;min-height:16px}.sc-weekday{padding:8px 4px;font-size:.75rem}.sc-week-header{min-height:50px}.sc-week-view{grid-template-columns:60px repeat(7,1fr)}.sc-week-view.sc-fullday{grid-template-columns:repeat(7,1fr)}.sc-day-header{padding:8px 4px}.sc-day-name{font-size:.625rem}.sc-day-header .sc-day-number{font-size:1rem}.sc-day-header.sc-today .sc-day-number{color:var(--sc-today-text);font-weight:600}.sc-time-slot{height:40px;padding:4px;font-size:.625rem}.sc-hour-slot{height:40px}.sc-day-view{grid-template-columns:60px 1fr;max-height:calc(100vh - 160px)}.sc-week-view{max-height:calc(100vh - 160px)}}@media (max-width:480px){.sc-calendar{border-radius:0;box-shadow:none;border:1px solid #e9ecef}.sc-header{padding:12px}.sc-btn{padding:6px 8px;font-size:.75rem;min-width:30px}.sc-title{font-size:1rem}.sc-day{min-height:50px;padding:2px}.sc-days-grid{min-height:0;grid-template-rows:repeat(6,1fr)}.sc-day-count-mode .sc-day-number{font-size:.875rem}.sc-event-count{font-size:.5rem;padding:1px 3px;min-width:12px}.sc-weekday{padding:6px 2px;font-size:.625rem}.sc-week-header{min-height:40px}.sc-week-view{grid-template-columns:50px repeat(7,1fr);max-height:calc(100vh - 140px)}.sc-week-view.sc-fullday{grid-template-columns:repeat(7,1fr)}.sc-time-slot{height:30px;padding:2px;font-size:.5rem}.sc-hour-slot{height:30px}.sc-day-view{grid-template-columns:50px 1fr;max-height:calc(100vh - 140px)}.sc-day-view .sc-event-time,.sc-week-view .sc-event-time{top:2px;left:2px;right:2px;padding:1px 3px;padding-top:2px;padding-bottom:0;font-size:.5rem;min-height:14px}}.sc-calendar[style*=height] .sc-event-day,.sc-calendar[style*=height] .sc-event-month,.sc-calendar[style*=height] .sc-event-text,.sc-calendar[style*=height] .sc-event-week{max-width:calc(100% - 12px);overflow:hidden;text-overflow:ellipsis;white-space:nowrap;word-break:break-word}.sc-calendar[style*="height: 350px"],.sc-calendar[style*="height: 380px"],.sc-calendar[style*="height: 420px"]{--sc-event-text-max-width:calc(100% - 8px)}.sc-calendar[style*="height: 350px"] .sc-event-day,.sc-calendar[style*="height: 350px"] .sc-event-month,.sc-calendar[style*="height: 350px"] .sc-event-text,.sc-calendar[style*="height: 350px"] .sc-event-week,.sc-calendar[style*="height: 380px"] .sc-event-day,.sc-calendar[style*="height: 380px"] .sc-event-month,.sc-calendar[style*="height: 380px"] .sc-event-text,.sc-calendar[style*="height: 380px"] .sc-event-week,.sc-calendar[style*="height: 420px"] .sc-event-day,.sc-calendar[style*="height: 420px"] .sc-event-month,.sc-calendar[style*="height: 420px"] .sc-event-text,.sc-calendar[style*="height: 420px"] .sc-event-week{max-width:calc(100% - 8px);padding-left:3px;padding-right:3px}.sc-calendar[style*="height: 350px"].sc-fullday .sc-event-text,.sc-calendar[style*="height: 380px"].sc-fullday .sc-event-text,.sc-calendar[style*="height: 420px"].sc-fullday .sc-event-text{width:calc(100% - 8px);max-width:calc(100% - 8px);left:4px;right:4px;font-size:.65rem}.sc-calendar[style*="height: 350px"] .sc-fullday .sc-event-text,.sc-calendar[style*="height: 380px"] .sc-fullday .sc-event-text,.sc-calendar[style*="height: 420px"] .sc-fullday .sc-event-text{width:calc(100% - 8px);max-width:calc(100% - 8px);left:4px;right:4px;font-size:.65rem}.sc-borders-both .sc-month-view .sc-days-grid{border-left:1px solid var(--sc-border-color);border-bottom:1px solid var(--sc-border-color)}.sc-borders-vertical .sc-month-view .sc-days-grid{border-left:1px solid var(--sc-border-color);border-bottom:none}.sc-borders-horizontal .sc-month-view .sc-days-grid{border-left:none;border-bottom:1px solid var(--sc-border-color)}.sc-borders-none .sc-month-view .sc-days-grid{border-left:none;border-bottom:none}.sc-borders-both .sc-day{border-right:1px solid var(--sc-border-color);border-top:1px solid var(--sc-border-color)}.sc-borders-vertical .sc-day{border-right:1px solid var(--sc-border-color);border-top:none}.sc-borders-horizontal .sc-day{border-right:none;border-top:1px solid var(--sc-border-color)}.sc-borders-none .sc-day{border-right:none;border-top:none}.sc-borders-both .sc-day-column,.sc-borders-both .sc-time-slot{border-right:1px solid var(--sc-border-color);border-bottom:1px solid var(--sc-border-color)}.sc-borders-vertical .sc-day-column,.sc-borders-vertical .sc-time-slot{border-right:1px solid var(--sc-border-color);border-bottom:none}.sc-borders-horizontal .sc-day-column,.sc-borders-horizontal .sc-time-slot{border-right:none;border-bottom:1px solid var(--sc-border-color)}.sc-borders-none .sc-day-column,.sc-borders-none .sc-time-slot{border-right:none;border-bottom:none}.sc-week-view:not(.sc-fullday) .sc-time-slot{border-bottom:1px solid var(--sc-border-color)!important}.sc-week-view:not(.sc-fullday) .sc-day-column{border-bottom:1px solid var(--sc-border-color)!important}.sc-borders-both .sc-week-view.sc-fullday .sc-day{border-right:1px solid var(--sc-border-color);border-bottom:1px solid var(--sc-border-color)}.sc-borders-both .sc-week-view.sc-fullday .sc-day:first-child{border-left:1px solid var(--sc-border-color)}.sc-borders-vertical .sc-week-view.sc-fullday .sc-day{border-right:1px solid var(--sc-border-color);border-bottom:none}.sc-borders-horizontal .sc-week-view.sc-fullday .sc-day{border-right:none;border-bottom:1px solid var(--sc-border-color)}.sc-borders-none .sc-week-view.sc-fullday .sc-day{border-right:none;border-bottom:none}.sc-borders-both .sc-day-header,.sc-borders-both .sc-time-column-header{border-right:1px solid var(--sc-border-color);border-bottom:1px solid var(--sc-border-color)}.sc-borders-both .sc-day-header:first-child,.sc-borders-both .sc-time-column-header:first-child,.sc-borders-vertical .sc-day-header:first-child,.sc-borders-vertical .sc-time-column-header:first-child{border-left:1px solid var(--sc-border-color)}.sc-borders-vertical .sc-day-header,.sc-borders-vertical .sc-time-column-header{border-right:1px solid var(--sc-border-color);border-bottom:none}.sc-borders-horizontal .sc-day-header,.sc-borders-horizontal .sc-time-column-header{border-right:none;border-bottom:1px solid var(--sc-border-color)}.sc-borders-none .sc-day-header,.sc-borders-none .sc-time-column-header{border-right:none;border-bottom:none}.sc-borders-both .sc-week-view:not(.sc-fullday){border-left:1px solid var(--sc-border-color);border-right:1px solid var(--sc-border-color)}.sc-borders-vertical .sc-week-view:not(.sc-fullday){border-left:1px solid var(--sc-border-color);border-right:1px solid var(--sc-border-color)}.sc-borders-both .sc-day-header:nth-child(8n),.sc-borders-vertical .sc-day-header:nth-child(8n){border-right:none}.sc-borders-both .sc-half-hour-line,.sc-borders-horizontal .sc-half-hour-line{border-top:1px dotted var(--sc-border-color)}.sc-borders-none .sc-half-hour-line,.sc-borders-vertical .sc-half-hour-line{border-top:none}.sc-week-view:not(.sc-fullday) .sc-half-hour-line{border-top:1px dotted var(--sc-border-color)!important}.sc-day-view:not(.sc-fullday) .sc-time-slot{border-bottom:1px solid var(--sc-border-color)!important}.sc-borders-both .sc-day-view:not(.sc-fullday) .sc-time-slot,.sc-borders-vertical .sc-day-view:not(.sc-fullday) .sc-time-slot{border-left:1px solid var(--sc-border-color)!important}.sc-day-view:not(.sc-fullday) .sc-day-column{border-bottom:1px solid var(--sc-border-color)!important}.sc-day-view:not(.sc-fullday) .sc-half-hour-line{border-top:1px dotted var(--sc-border-color)!important}.sc-borders-both .sc-month-view .sc-days-grid,.sc-borders-vertical .sc-month-view .sc-days-grid{border-right:1px solid var(--sc-border-color)}.sc-borders-both .sc-day:nth-child(7n),.sc-borders-vertical .sc-day:nth-child(7n){border-right:none}:root{--sc-event-border-color:#6c757d}.sc-event-borders .sc-event-day:not(.sc-event-placeholder),.sc-event-borders .sc-event-month:not(.sc-event-placeholder),.sc-event-borders .sc-event-time:not(.sc-event-placeholder),.sc-event-borders .sc-event-week:not(.sc-event-placeholder){border:1px solid var(--sc-event-border-color)}.sc-event-borders .sc-event-single:not(.sc-event-placeholder){border:1px solid var(--sc-event-border-color)}.sc-event-borders .sc-event-start:not(.sc-event-placeholder){border-left:1px solid var(--sc-event-border-color);border-top:1px solid var(--sc-event-border-color);border-bottom:1px solid var(--sc-event-border-color)}.sc-event-borders .sc-event-middle:not(.sc-event-placeholder){border-top:1px solid var(--sc-event-border-color);border-bottom:1px solid var(--sc-event-border-color)}.sc-event-borders .sc-event-end:not(.sc-event-placeholder){border-right:1px solid var(--sc-event-border-color);border-top:1px solid var(--sc-event-border-color);border-bottom:1px solid var(--sc-event-border-color)}.sc-week-view .sc-event-week{border-right:none}.sc-event-no-borders .sc-event-day,.sc-event-no-borders .sc-event-month,.sc-event-no-borders .sc-event-time,.sc-event-no-borders .sc-event-week{border:none!important}@media print{.sc-calendar{box-shadow:none;border:1px solid #000}.sc-header{background:#fff!important;-webkit-print-color-adjust:exact;print-color-adjust:exact}.sc-btn{display:none}.sc-view-switcher{display:none}}
@@ -1,5 +1,5 @@
1
1
  /**
2
- * SimpleCalendarJs v2.0.2
2
+ * SimpleCalendarJs v2.0.3
3
3
  * A clean, modern, and feature-rich JavaScript calendar component with zero dependencies
4
4
  *
5
5
  * @author Pedro Lopes <simplecalendarjs@gmail.com>
@@ -7,4 +7,4 @@
7
7
  * @license SEE LICENSE IN LICENSE
8
8
  * @repository https://github.com/pclslopes/SimpleCalendarJs
9
9
  */
10
- class t{constructor(t,e={}){this.container="string"==typeof t?document.querySelector(t):t;const s=this.mergeColors({background:"#ffffff",backgroundSecondary:"#f8f9fa",backgroundTertiary:"#ecf0f1",text:"#212529",textSecondary:"#6c757d",textMuted:"#adb5bd",border:"#e9ecef",borderLight:"#dee2e6",accent:"#4c6f94",accentHover:"#0056b3",todayBg:"#e3f2fd",todayText:"#dc3545",hoverBg:"#f8f9fa",dark:{background:"#2d2d2d",backgroundSecondary:"#3a3a3a",backgroundTertiary:"#4a4a4a",text:"#ffffff",textSecondary:"#cccccc",textMuted:"#888888",border:"#444444",borderLight:"#555555",accent:"#4a90e2",accentHover:"#357abd",todayBg:"#1a2f4a",todayText:"#ff6b6b",hoverBg:"#2d2d2d"}},e.colors||{});this.options={view:"month",date:new Date,events:[],fulldayMode:!1,startHour:6,endHour:22,timeSlotMinutes:30,dayClick:null,eventClick:null,changeState:null,gridBorders:"both",eventBorders:!1,eventBorderColor:"#6c757d",defaultEventColor:"#4c6f94",showMonthButton:!0,showWeekButton:!0,showDayButton:!0,showNavigation:!0,weekdays:["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"],months:["January","February","March","April","May","June","July","August","September","October","November","December"],showWeekdayChars:null,labels:{month:"Month",week:"Week",day:"Day",events:"events",event:"event",before:"Before",after:"After"},colors:s,...e,colors:s},this.currentDate=new Date(this.options.date),this.view=this.options.view,this.events=this.options.events,this.eventsCallback="function"==typeof this.options.events?this.options.events:null,this.fulldayMode=this.options.fulldayMode,this.fetchedRanges=[],this.allCachedEvents=[],this.init()}mergeColors(t,e){const s={...t};return Object.keys(e).forEach(t=>{"dark"!==t&&void 0!==e[t]&&(s[t]=e[t])}),e.dark&&(s.dark={...t.dark},Object.keys(e.dark).forEach(t=>{void 0!==e.dark[t]&&(s.dark[t]=e.dark[t])})),s}init(){this.container.innerHTML=this.getCalendarTemplate(),this.applyGridBorders(),this.applyEventBorders(),this.applyEventColor(),this.applyViewButtonVisibility(),this.applyNavigationVisibility(),this.applyCustomColors(),this.bindEvents(),this.render()}getWeekdayDisplay(t){const e=this.options.weekdays[t];return null===this.options.showWeekdayChars?e:e.substring(0,this.options.showWeekdayChars)}triggerChangeState(){if(this.options.changeState){const t={view:this.view,date:new Date(this.currentDate),fulldayMode:this.fulldayMode,startHour:this.options.startHour,endHour:this.options.endHour,timeSlotMinutes:this.options.timeSlotMinutes};this.options.changeState(t)}}applyGridBorders(){const t=this.container.querySelector(".sc-calendar");t&&(t.classList.remove("sc-borders-both","sc-borders-vertical","sc-borders-horizontal","sc-borders-none"),t.classList.add(`sc-borders-${this.options.gridBorders}`))}applyEventBorders(){const t=this.container.querySelector(".sc-calendar");t&&(t.classList.remove("sc-event-borders","sc-event-no-borders"),this.options.eventBorders?(t.classList.add("sc-event-borders"),t.style.setProperty("--sc-event-border-color",this.options.eventBorderColor)):t.classList.add("sc-event-no-borders"))}applyEventColor(){const t=this.container.querySelector(".sc-calendar");t&&t.style.setProperty("--sc-event-color",this.options.defaultEventColor)}applyViewButtonVisibility(){const t=this.container.querySelector('[data-view="month"]'),e=this.container.querySelector('[data-view="week"]'),s=this.container.querySelector('[data-view="day"]');t&&(t.style.display=this.options.showMonthButton?"flex":"none"),e&&(e.style.display=this.options.showWeekButton?"flex":"none"),s&&(s.style.display=this.options.showDayButton?"flex":"none");const n=this.container.querySelector(".sc-view-switcher");if(n){const t=this.options.showMonthButton||this.options.showWeekButton||this.options.showDayButton;n.style.display=t?"flex":"none"}}applyNavigationVisibility(){const t=this.container.querySelector(".sc-nav");t&&(t.style.visibility=this.options.showNavigation?"visible":"hidden")}applyCustomColors(){if(!this.container.querySelector(".sc-calendar"))return;const t=this.options.colors,e=(t,e)=>void 0!==e?`--${t}: ${e} !important;`:"",s=`\n .sc-calendar {\n ${e("sc-bg-primary",t.background)}\n ${e("sc-bg-secondary",t.backgroundSecondary)}\n ${e("sc-bg-tertiary",t.backgroundTertiary)}\n ${e("sc-text-primary",t.text)}\n ${e("sc-text-secondary",t.textSecondary)}\n ${e("sc-text-muted",t.textMuted)}\n ${e("sc-border-color",t.border)}\n ${e("sc-border-light",t.borderLight)}\n ${e("sc-accent-color",t.accent)}\n ${e("sc-accent-hover",t.accentHover)}\n ${e("sc-today-bg",t.todayBg)}\n ${e("sc-today-text",t.todayText)}\n ${e("sc-hover-bg",t.hoverBg)}\n }\n\n ${t.dark?`\n [data-theme="dark"] .sc-calendar {\n ${e("sc-bg-primary",t.dark.background)}\n ${e("sc-bg-secondary",t.dark.backgroundSecondary)}\n ${e("sc-bg-tertiary",t.dark.backgroundTertiary)}\n ${e("sc-text-primary",t.dark.text)}\n ${e("sc-text-secondary",t.dark.textSecondary)}\n ${e("sc-text-muted",t.dark.textMuted)}\n ${e("sc-border-color",t.dark.border)}\n ${e("sc-border-light",t.dark.borderLight)}\n ${e("sc-accent-color",t.dark.accent)}\n ${e("sc-accent-hover",t.dark.accentHover)}\n ${e("sc-today-bg",t.dark.todayBg)}\n ${e("sc-today-text",t.dark.todayText)}\n ${e("sc-hover-bg",t.dark.hoverBg)}\n }\n `:""}\n `;let n=document.getElementById("sc-custom-colors");n||(n=document.createElement("style"),n.id="sc-custom-colors",document.head.appendChild(n)),n.textContent=s}getCalendarTemplate(){return`\n <div class="sc-calendar">\n <div class="sc-header">\n <div class="sc-nav">\n <button class="sc-btn sc-prev">←</button>\n <button class="sc-btn sc-next">→</button>\n </div>\n <div class="sc-title-container">\n <h2 class="sc-title">\n <span class="sc-title-month"></span>\n <span class="sc-title-year"></span>\n </h2>\n <div class="sc-month-dropdown" style="display: none;">\n ${this.options.months.map((t,e)=>`<div class="sc-month-option" data-month="${e}">${t}</div>`).join("")}\n </div>\n <div class="sc-year-dropdown" style="display: none;">\n </div>\n </div>\n <div class="sc-view-switcher">\n <button class="sc-btn sc-view-btn ${"month"===this.view?"sc-active":""}" data-view="month">${this.options.labels.month}</button>\n <button class="sc-btn sc-view-btn ${"week"===this.view?"sc-active":""}" data-view="week">${this.options.labels.week}</button>\n <button class="sc-btn sc-view-btn ${"day"===this.view?"sc-active":""}" data-view="day">${this.options.labels.day}</button>\n </div>\n </div>\n <div class="sc-content">\n <div class="sc-view-container"></div>\n </div>\n </div>\n `}bindEvents(){const t=this.container.querySelector(".sc-prev"),e=this.container.querySelector(".sc-next"),s=this.container.querySelectorAll(".sc-view-btn");t.addEventListener("click",()=>this.navigate("prev")),e.addEventListener("click",()=>this.navigate("next")),s.forEach(t=>{t.addEventListener("click",t=>{const e=t.target.dataset.view;this.setView(e)})}),this.container.addEventListener("click",t=>{this.handleCalendarClick(t)}),this.outsideClickBound||(document.addEventListener("click",t=>{t.target.closest(".sc-title-container")||(this.hideMonthDropdown(),this.hideYearDropdown())}),this.outsideClickBound=!0)}bindMonthViewDropdowns(){if("month"!==this.view)return;const t=this.container.querySelector(".sc-title-month"),e=this.container.querySelector(".sc-title-year");if(t){const e=t.cloneNode(!0);t.parentNode.replaceChild(e,t),e.addEventListener("click",t=>{t.stopPropagation(),this.hideYearDropdown(),this.toggleMonthDropdown()})}if(e){const t=e.cloneNode(!0);e.parentNode.replaceChild(t,e),t.addEventListener("click",t=>{t.stopPropagation(),this.hideMonthDropdown(),this.toggleYearDropdown()})}this.generateYearOptions(),this.dropdownOptionsHandler&&this.container.removeEventListener("click",this.dropdownOptionsHandler),this.dropdownOptionsHandler=t=>{if(t.target.classList.contains("sc-month-option")){const e=parseInt(t.target.getAttribute("data-month"));this.setMonth(e),this.hideMonthDropdown()}else if(t.target.classList.contains("sc-year-option")){const e=parseInt(t.target.getAttribute("data-year"));this.setYear(e),this.hideYearDropdown()}},this.container.addEventListener("click",this.dropdownOptionsHandler);this.container.querySelectorAll(".sc-month-option"),this.container.querySelectorAll(".sc-year-option")}calculatePreciseTime(t,e){const s=t.getAttribute("data-time");if(!s)return null;const n=t.getBoundingClientRect(),i=e.clientY-n.top>n.height/2,a=this.parseTimeString(s);return a?(i&&a.setMinutes(a.getMinutes()+30),this.formatTime(a)):s}handleCalendarClick(t){if(t.target.classList.contains("sc-title-month")||t.target.classList.contains("sc-title-year")||t.target.classList.contains("sc-month-option")||t.target.classList.contains("sc-year-option")||t.target.closest(".sc-month-dropdown")||t.target.closest(".sc-year-dropdown"))return;const e=t.target.closest("[data-event-id]");if(e)return t.stopPropagation(),void this.handleEventClick(e,t);const s=t.target.closest("[data-date]");s&&this.handleDayClick(s,t)}handleEventClick(t,e){if(!this.options.eventClick)return;const s=t.getAttribute("data-event-id"),n=t.closest("[data-date]"),i=n?n.getAttribute("data-date"):null;if(s&&i){const t=new Date(i),a=this.getAllEventsForRange().find(t=>t.id==s);if(a){const s={date:t,time:this.calculatePreciseTime(n,e)};this.options.eventClick(a,s,e)}}}handleDayClick(t,e){if(!this.options.dayClick)return;const s=t.getAttribute("data-date");if(s){const n={date:new Date(s),time:this.calculatePreciseTime(t,e)};this.options.dayClick(n,e)}}navigate(t){const e="next"===t?1:-1;switch(this.view){case"month":this.currentDate.setMonth(this.currentDate.getMonth()+e);break;case"week":this.currentDate.setDate(this.currentDate.getDate()+7*e);break;case"day":this.currentDate.setDate(this.currentDate.getDate()+e)}this.render(),this.triggerChangeState()}toggleMonthDropdown(){if("month"!==this.view)return;const t=this.container.querySelector(".sc-month-dropdown");if(t){"none"!==t.style.display?this.hideMonthDropdown():(this.updateMonthDropdownSelection(),t.style.display="block",this.scrollToActiveMonth())}}hideMonthDropdown(){const t=this.container.querySelector(".sc-month-dropdown");t&&(t.style.display="none")}updateMonthDropdownSelection(){const t=this.container.querySelectorAll(".sc-month-option"),e=this.currentDate.getMonth();t.forEach((t,s)=>{t.classList.remove("sc-current"),s===e&&t.classList.add("sc-current")})}toggleYearDropdown(){if("month"!==this.view)return;const t=this.container.querySelector(".sc-year-dropdown");if(t){"none"!==t.style.display?this.hideYearDropdown():(this.generateYearOptions(),this.updateYearDropdownSelection(),t.style.display="block",this.scrollToActiveYear())}}hideYearDropdown(){const t=this.container.querySelector(".sc-year-dropdown");t&&(t.style.display="none")}generateYearOptions(){const t=this.container.querySelector(".sc-year-dropdown"),e=this.currentDate.getFullYear(),s=e+10;if(t){let n="";for(let t=e-10;t<=s;t++)n+=`<div class="sc-year-option" data-year="${t}">${t}</div>`;t.innerHTML=n}}updateYearDropdownSelection(){const t=this.container.querySelectorAll(".sc-year-option"),e=this.currentDate.getFullYear();t.forEach(t=>{t.classList.remove("sc-current"),parseInt(t.getAttribute("data-year"))===e&&t.classList.add("sc-current")})}setYear(t){this.currentDate.setFullYear(t),this.render(),this.triggerChangeState()}scrollToActiveMonth(){const t=this.container.querySelector(".sc-month-dropdown"),e=t?.querySelector(".sc-month-option.sc-current");t&&e&&setTimeout(()=>{t.getBoundingClientRect(),e.getBoundingClientRect(),t.scrollTop;const s=e.offsetTop-t.clientHeight/2+e.offsetHeight/2;t.scrollTop=s},0)}scrollToActiveYear(){const t=this.container.querySelector(".sc-year-dropdown"),e=t?.querySelector(".sc-year-option.sc-current");t&&e&&setTimeout(()=>{t.getBoundingClientRect(),e.getBoundingClientRect(),t.scrollTop;const s=e.offsetTop-t.clientHeight/2+e.offsetHeight/2;t.scrollTop=s},0)}setMonth(t){this.currentDate.setMonth(t),this.render(),this.triggerChangeState()}setView(t){this.view=t,this.container.querySelectorAll(".sc-view-btn").forEach(t=>{t.classList.remove("sc-active")}),this.container.querySelector(`[data-view="${t}"]`).classList.add("sc-active"),this.hideMonthDropdown(),this.hideYearDropdown(),this.render(),this.triggerChangeState()}render(){this.updateTitle(),this.renderView(),"month"===this.view&&this.bindMonthViewDropdowns()}updateTitle(){const t=this.container.querySelector(".sc-title"),e=this.container.querySelector(".sc-title-month"),s=this.container.querySelector(".sc-title-year");switch(this.view){case"month":e&&s?(e.textContent=this.options.months[this.currentDate.getMonth()],s.textContent=this.currentDate.getFullYear()):t.innerHTML=`<span class="sc-title-month">${this.options.months[this.currentDate.getMonth()]}</span> <span class="sc-title-year">${this.currentDate.getFullYear()}</span>`;break;case"week":const n=this.getWeekStart(this.currentDate),i=new Date(n);i.setDate(i.getDate()+6),t.textContent=`${this.options.months[n.getMonth()]} ${n.getDate()} - ${this.options.months[i.getMonth()]} ${i.getDate()}, ${n.getFullYear()}`;break;case"day":t.textContent=`${this.options.weekdays[this.currentDate.getDay()]}, ${this.options.months[this.currentDate.getMonth()]} ${this.currentDate.getDate()}, ${this.currentDate.getFullYear()}`}}renderView(){const t=this.container.querySelector(".sc-view-container");switch(this.view){case"month":t.innerHTML=this.renderMonthView();break;case"week":t.innerHTML=this.renderWeekView();break;case"day":t.innerHTML=this.renderDayView()}}renderMonthView(){const t=this.currentDate.getFullYear(),e=this.currentDate.getMonth(),s=new Date(t,e,1),n=new Date(s);n.setDate(n.getDate()-s.getDay());let i=`\n <div class="sc-month-view ${this.fulldayMode?"sc-fullday":""}">\n <div class="sc-weekdays">\n ${this.options.weekdays.map((t,e)=>`<div class="sc-weekday">${this.getWeekdayDisplay(e)}</div>`).join("")}\n </div>\n <div class="sc-days-grid">\n `;for(let t=0;t<6;t++)for(let s=0;s<7;s++){const a=new Date(n);a.setDate(n.getDate()+7*t+s);const o=a.getMonth()===e,r=this.isToday(a),c=this.getEventsForDate(a);if(this.fulldayMode){const t=this.addEventPlaceholders(c,a);i+=`\n <div class="sc-day ${o?"sc-current-month":"sc-other-month"} ${r?"sc-today":""}" data-date="${a.toISOString()}">\n <div class="sc-day-number">${a.getDate()}</div>\n ${t.map(t=>{if(t.isPlaceholder)return'<div class="sc-event-month sc-event-single sc-event-placeholder">&nbsp;</div>';const e=this.getEventPosition(t.event,a),s=e.isSingle?"sc-event-single":e.isStart?"sc-event-start":e.isEnd?"sc-event-end":"sc-event-middle",n=e.isSingle||e.isStart?t.event.title:"",i=this.applyEventStyles(t.event);return`<div class="sc-event-month ${s}" data-event-id="${t.event.id}" ${i}><span class="sc-event-text">${n}</span></div>`}).join("")}\n </div>\n `}else{const t=c.length,e=0===t?"":1===t?`1 ${this.options.labels.event}`:`${t} ${this.options.labels.events}`;i+=`\n <div class="sc-day sc-day-count-mode ${o?"sc-current-month":"sc-other-month"} ${r?"sc-today":""}" data-date="${a.toISOString()}">\n <div class="sc-day-content">\n <div class="sc-day-number">${a.getDate()}</div>\n ${e?`<div class="sc-event-count">${e}</div>`:""}\n </div>\n </div>\n `}}return i+="\n </div>\n </div>\n ",i}renderWeekView(){const t=this.getWeekStart(this.currentDate),e=[];for(let s=0;s<7;s++){const n=new Date(t);n.setDate(n.getDate()+s),e.push(n)}if(this.fulldayMode)return this.renderWeekFullday(e);const s=this.generateTimeSlots();return`\n <div class="sc-week-view">\n <div class="sc-time-column-header"></div>\n ${e.map(t=>`\n <div class="sc-day-header ${this.isToday(t)?"sc-today":""}">\n <div class="sc-day-name">${this.getWeekdayDisplay(t.getDay())}</div>\n <div class="sc-day-number">${t.getDate()}</div>\n </div>\n `).join("")}\n ${s.map(t=>`\n <div class="sc-time-slot">${t}</div>\n ${e.map(e=>{const s=this.getEventsForTimeSlot(e,t);return`\n <div class="sc-day-column ${this.isToday(e)?"sc-today":""}" data-date="${e.toISOString()}" data-time="${t}">\n <div class="sc-half-hour-line"></div>\n ${s.map(t=>{const e=this.applyEventStyles(t);return`<div class="sc-event-time" data-event-id="${t.id}" ${e}>${t.title}</div>`}).join("")}\n </div>\n `}).join("")}\n `).join("")}\n </div>\n `}renderWeekFullday(t){return`\n <div class="sc-week-view sc-fullday">\n <div class="sc-weekdays">\n ${t.map(t=>`<div class="sc-weekday">${this.getWeekdayDisplay(t.getDay())}</div>`).join("")}\n </div>\n <div class="sc-days-grid">\n ${t.map(t=>{const e=this.isToday(t),s=this.getEventsForDate(t),n=this.addEventPlaceholders(s,t);return`\n <div class="sc-day ${e?"sc-today":""}" data-date="${t.toISOString()}">\n <div class="sc-day-number">${t.getDate()}</div>\n ${n.map(e=>{if(e.isPlaceholder)return'<div class="sc-event-week sc-event-single sc-event-placeholder">&nbsp;</div>';const s=this.getEventPosition(e.event,t),n=s.isSingle?"sc-event-single":s.isStart?"sc-event-start":s.isEnd?"sc-event-end":"sc-event-middle",i=s.isSingle||s.isStart?e.event.title:"",a=this.applyEventStyles(e.event);return`<div class="sc-event-week ${n}" data-event-id="${e.event.id}" ${a}><span class="sc-event-text">${i}</span></div>`}).join("")}\n </div>\n `}).join("")}\n </div>\n </div>\n `}renderDayView(){const t=new Date(this.currentDate);if(this.fulldayMode){const e=this.getEventsForDate(t);return`\n <div class="sc-day-view sc-fullday">\n <div class="sc-day-header ${this.isToday(t)?"sc-today":""}">\n <div class="sc-day-name">${this.options.weekdays[t.getDay()]}</div>\n <div class="sc-day-number">${t.getDate()}</div>\n </div>\n <div class="sc-day-events" data-date="${t.toISOString()}">\n ${e.map(e=>{const s=this.getEventPosition(e,t),n=s.isSingle?"sc-event-single":s.isStart?"sc-event-start":s.isEnd?"sc-event-end":"sc-event-middle",i=e.title,a=this.applyEventStyles(e);return`<div class="sc-event-day ${n}" data-event-id="${e.id}" ${a}><span class="sc-event-text">${i}</span></div>`}).join("")}\n </div>\n </div>\n `}const e=this.generateTimeSlots();return`\n <div class="sc-day-view">\n <div class="sc-time-column-header"></div>\n <div class="sc-day-header ${this.isToday(t)?"sc-today":""}">\n <div class="sc-day-name">${this.options.weekdays[t.getDay()]}</div>\n <div class="sc-day-number">${t.getDate()}</div>\n </div>\n ${e.map(e=>{const s=this.getEventsForTimeSlot(t,e);return`\n <div class="sc-time-slot">${e}</div>\n <div class="sc-day-column ${this.isToday(t)?"sc-today":""}" data-date="${t.toISOString()}" data-time="${e}">\n <div class="sc-half-hour-line"></div>\n ${s.map(t=>{const e=this.applyEventStyles(t);return`<div class="sc-event-time" data-event-id="${t.id}" ${e}>${t.title}</div>`}).join("")}\n </div>\n `}).join("")}\n </div>\n `}generateTimeSlots(){const t=[],e=this.options.startHour,s=this.options.endHour,n=new Date;n.setHours(e,0,0,0),t.push(`${this.options.labels.before} ${this.formatTime(n)}`);for(let n=e;n<=s;n++){const e=new Date;e.setHours(n,0,0,0),t.push(this.formatTime(e))}const i=new Date;return i.setHours(s,0,0,0),t.push(`${this.options.labels.after} ${this.formatTime(i)}`),t}formatTime(t){return t.toLocaleTimeString("en-US",{hour:"numeric",minute:"2-digit",hour12:!0})}getWeekStart(t){const e=new Date(t);return e.setDate(e.getDate()-e.getDay()),e}getVisibleDateRange(){const t=this.currentDate.getFullYear(),e=this.currentDate.getMonth();switch(this.view){case"month":const s=new Date(t,e,1),n=new Date(s);n.setDate(n.getDate()-s.getDay());const i=new Date(n);return i.setDate(i.getDate()+41),{startDate:n,endDate:i};case"week":const a=this.getWeekStart(this.currentDate),o=new Date(a);return o.setDate(o.getDate()+6),{startDate:a,endDate:o};case"day":const r=new Date(this.currentDate);r.setHours(0,0,0,0);const c=new Date(r);return c.setHours(23,59,59,999),{startDate:r,endDate:c}}}isToday(t){const e=new Date;return t.toDateString()===e.toDateString()}isRangeCovered(t,e){for(const s of this.fetchedRanges)if(s.startDate<=t&&s.endDate>=e)return!0;return!1}getMissingRanges(t,e){const s=[];let n=new Date(t);const i=[...this.fetchedRanges].sort((t,e)=>t.startDate-e.startDate);for(const t of i){if(n<t.startDate&&n<=e){const i=new Date(Math.min(t.startDate.getTime()-864e5,e.getTime()));i>=n&&s.push({startDate:new Date(n),endDate:i})}t.endDate>=n&&(n=new Date(t.endDate.getTime()+864e5))}return n<=e&&s.push({startDate:new Date(n),endDate:new Date(e)}),s}addFetchedRange(t,e,s){const n={startDate:new Date(t),endDate:new Date(e),events:s};this.allCachedEvents=this.allCachedEvents.concat(s),this.fetchedRanges.push(n),this.mergeOverlappingRanges()}mergeOverlappingRanges(){this.fetchedRanges.sort((t,e)=>t.startDate-e.startDate);const t=[];for(const e of this.fetchedRanges)if(0===t.length||t[t.length-1].endDate<e.startDate)t.push(e);else{const s=t[t.length-1];s.endDate=new Date(Math.max(s.endDate.getTime(),e.endDate.getTime())),s.events=s.events.concat(e.events)}this.fetchedRanges=t,this.allCachedEvents=[];for(const t of this.fetchedRanges)this.allCachedEvents=this.allCachedEvents.concat(t.events)}getAllEventsForRange(){if(this.eventsCallback){const t=this.getVisibleDateRange();if(this.isRangeCovered(t.startDate,t.endDate))return this.allCachedEvents;const e=this.getMissingRanges(t.startDate,t.endDate);for(const t of e){const e=this.eventsCallback(t);this.addFetchedRange(t.startDate,t.endDate,e)}return this.allCachedEvents}return this.events||[]}getEventsForDate(t){return this.getAllEventsForRange().filter(e=>{const s=new Date(e.startDate||e.date),n=new Date(e.endDate||e.date),i=new Date(t);return i.setHours(0,0,0,0),s.setHours(0,0,0,0),n.setHours(0,0,0,0),i>=s&&i<=n}).sort((t,e)=>{const s=this.isMultiDayEvent(t),n=this.isMultiDayEvent(e);return s&&!n?-1:!s&&n?1:0})}getEventsForTimeSlot(t,e){if(this.fulldayMode)return this.getEventsForDate(t);return this.getEventsForDate(t).filter(t=>{if(!t.time)return!1;const s=this.parseTimeString(t.time);if(!s)return!1;const n=s.getHours(),i=this.options.startHour,a=this.options.endHour;if(e.startsWith(this.options.labels.before))return n<i;if(e.startsWith(this.options.labels.after))return n>a;const o=this.parseTimeString(e);if(!o)return!1;return n===o.getHours()})}parseTimeString(t){try{const e=new Date,[s,n]=t.split(" "),[i,a]=s.split(":").map(Number);let o=i;return"PM"===n&&12!==i&&(o+=12),"AM"===n&&12===i&&(o=0),e.setHours(o,a||0,0,0),e}catch(t){return null}}isMultiDayEvent(t){const e=new Date(t.startDate||t.date),s=new Date(t.endDate||t.date);return e.setHours(0,0,0,0),s.setHours(0,0,0,0),e.getTime()!==s.getTime()}addEventPlaceholders(t,e){if(!this.fulldayMode)return t.map(t=>({event:t,isPlaceholder:!1}));const s=this.getWeekStart(e),n=[];for(let t=0;t<7;t++){const e=new Date(s);e.setDate(e.getDate()+t),n.push(e)}const i=new Map;n.forEach(t=>{this.getEventsForDate(t).filter(t=>this.isMultiDayEvent(t)).forEach(t=>{i.has(t.id)||i.set(t.id,t)})});const a=Array.from(i.values()).sort((t,e)=>new Date(t.startDate||t.date)-new Date(e.startDate||e.date)),o=[],r=t.filter(t=>this.isMultiDayEvent(t)),c=t.filter(t=>!this.isMultiDayEvent(t));return a.forEach(t=>{const n=r.find(e=>e.id===t.id);if(n)o.push({event:n,isPlaceholder:!1});else{const n=new Date(t.startDate||t.date),i=new Date(t.endDate||t.date);n.setHours(0,0,0,0),i.setHours(0,0,0,0);const a=new Date(e);a.setHours(0,0,0,0);const r=new Date(s);r.setHours(0,0,0,0);const c=new Date(s);c.setDate(c.getDate()+6),c.setHours(0,0,0,0);n<=c&&(i>=r&&i<=c)&&a>i&&o.push({isPlaceholder:!0,eventId:t.id})}}),c.forEach(t=>{o.push({event:t,isPlaceholder:!1})}),o}getEventPosition(t,e){const s=new Date(t.startDate||t.date),n=new Date(t.endDate||t.date),i=new Date(e);s.setHours(0,0,0,0),n.setHours(0,0,0,0),i.setHours(0,0,0,0);return{isStart:i.getTime()===s.getTime(),isEnd:i.getTime()===n.getTime(),isMiddle:i>s&&i<n,isSingle:s.getTime()===n.getTime()}}addEvent(t){this.eventsCallback||(this.events.push(t),this.render())}removeEvent(t){this.eventsCallback||(this.events=this.events.filter(e=>e.id!==t),this.render())}setEvents(t){"function"==typeof t?(this.eventsCallback=t,this.events=[]):(this.events=t||[],this.eventsCallback=null),this.fetchedRanges=[],this.allCachedEvents=[],this.render()}setFulldayMode(t){this.fulldayMode=t,this.render(),this.triggerChangeState()}goToDate(t){this.currentDate=new Date(t),this.render(),this.triggerChangeState()}goToToday(){this.currentDate=new Date,this.render(),this.triggerChangeState()}setGridBorders(t){this.options.gridBorders=t,this.applyGridBorders()}setEventBorders(t){this.options.eventBorders=t,this.applyEventBorders(),this.render()}setEventBorderColor(t){this.options.eventBorderColor=t,this.applyEventBorders(),this.render()}setShowMonthButton(t){this.options.showMonthButton=t,this.applyViewButtonVisibility()}setShowWeekButton(t){this.options.showWeekButton=t,this.applyViewButtonVisibility()}setShowDayButton(t){this.options.showDayButton=t,this.applyViewButtonVisibility()}setViewButtonsVisibility(t,e,s){this.options.showMonthButton=t,this.options.showWeekButton=e,this.options.showDayButton=s,this.applyViewButtonVisibility()}setShowNavigation(t){this.options.showNavigation=t,this.applyNavigationVisibility()}setDefaultEventColor(t){this.options.defaultEventColor=t,this.applyEventColor(),this.render()}getEventBaseCssStyle(t){const e=t.color,s=!0===t.colorIsGradient,n=e?t.color:this.options.defaultEventColor;if(!n)return{};const i=(" "+n).slice(1),a=this.hexToRgb(i);if(!a)return{};const o=this.getOptimalTextColor(a);if(s){const t={r:Math.min(255,a.r+40),g:Math.min(255,a.g+40),b:Math.min(255,a.b+40)};return{"background-image":`linear-gradient(to bottom, ${this.rgbToHex(t)} 0px, ${n} 100%)`,"background-repeat":"repeat-x",color:o,"background-color":n}}return{"background-color":n,color:o}}getOptimalTextColor(t){return this.getLuminance(t)>.5?"#000000":"#ffffff"}getLuminance(t){return.2126*this.getRelativeLuminanceComponent(t.r/255)+.7152*this.getRelativeLuminanceComponent(t.g/255)+.0722*this.getRelativeLuminanceComponent(t.b/255)}getRelativeLuminanceComponent(t){return t<=.03928?t/12.92:Math.pow((t+.055)/1.055,2.4)}getContrastRatio(t,e){const s=this.getLuminance(t),n=this.getLuminance(e);return(Math.max(s,n)+.05)/(Math.min(s,n)+.05)}hexToRgb(t){const e=/^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(t);return e?{r:parseInt(e[1],16),g:parseInt(e[2],16),b:parseInt(e[3],16)}:null}rgbToHex(t){return"#"+(1<<24|t.r<<16|t.g<<8|t.b).toString(16).slice(1)}applyEventStyles(t){const e=this.getEventBaseCssStyle(t),s=Object.entries(e).map(([t,e])=>`${t}: ${e}`).join("; ");return s?`style="${s}"`:""}}
10
+ class t{constructor(t,e={}){this.container="string"==typeof t?document.querySelector(t):t;const s=this.mergeColors({background:"#ffffff",backgroundSecondary:"#f8f9fa",backgroundTertiary:"#ecf0f1",text:"#212529",textSecondary:"#6c757d",textMuted:"#adb5bd",border:"#e9ecef",borderLight:"#dee2e6",accent:"#4c6f94",accentHover:"#0056b3",todayBg:"#e3f2fd",todayText:"#dc3545",hoverBg:"#f8f9fa",dark:{background:"#2d2d2d",backgroundSecondary:"#3a3a3a",backgroundTertiary:"#4a4a4a",text:"#ffffff",textSecondary:"#cccccc",textMuted:"#888888",border:"#444444",borderLight:"#555555",accent:"#4a90e2",accentHover:"#357abd",todayBg:"#1a2f4a",todayText:"#ff6b6b",hoverBg:"#2d2d2d"}},e.colors||{});this.options={view:"month",date:new Date,events:[],fulldayMode:!1,startHour:6,endHour:22,timeSlotMinutes:30,dayClick:null,eventClick:null,changeState:null,gridBorders:"both",eventBorders:!1,eventBorderColor:"#6c757d",defaultEventColor:"#4c6f94",showMonthButton:!0,showWeekButton:!0,showDayButton:!0,showNavigation:!0,showTitle:!0,showMonth:!0,showYear:!0,weekdays:["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"],months:["January","February","March","April","May","June","July","August","September","October","November","December"],showWeekdayChars:null,labels:{month:"Month",week:"Week",day:"Day",events:"events",event:"event",before:"Before",after:"After"},colors:s,...e,colors:s},this.currentDate=new Date(this.options.date),this.view=this.options.view,this.events=this.options.events,this.eventsCallback="function"==typeof this.options.events?this.options.events:null,this.fulldayMode=this.options.fulldayMode,this.fetchedRanges=[],this.allCachedEvents=[],this.setupThemeDetection(),this.init()}detectTheme(){const t=document.documentElement.dataset.theme;if(t)return t;if(document.documentElement.classList.contains("dark"))return"dark";if(document.documentElement.classList.contains("light"))return"light";if(document.body.classList.contains("dark"))return"dark";if(document.body.classList.contains("light"))return"light";const e=getComputedStyle(document.documentElement).getPropertyValue("--theme");if(e)return e.trim();const s=document.documentElement.getAttribute("theme");if(s)return s;const n=document.body.dataset.theme;return n||(window.matchMedia&&window.matchMedia("(prefers-color-scheme: dark)").matches?"dark":"light")}applyDetectedTheme(){const t=this.detectTheme(),e=this.container.querySelector(".sc-calendar");e&&(e.removeAttribute("data-theme"),e.setAttribute("data-theme",t))}setupThemeDetection(){this.applyDetectedTheme(),"undefined"!=typeof MutationObserver&&(this.themeObserver=new MutationObserver(t=>{let e=!1;t.forEach(t=>{if("attributes"===t.type){t.target;const s=t.attributeName;"data-theme"!==s&&"theme"!==s&&"class"!==s||(e=!0)}}),e&&this.applyDetectedTheme()}),this.themeObserver.observe(document.documentElement,{attributes:!0,attributeFilter:["data-theme","theme","class"]}),this.themeObserver.observe(document.body,{attributes:!0,attributeFilter:["data-theme","theme","class"]})),window.matchMedia&&(this.systemThemeListener=t=>{this.applyDetectedTheme()},window.matchMedia("(prefers-color-scheme: dark)").addEventListener("change",this.systemThemeListener))}mergeColors(t,e){const s={...t};return Object.keys(e).forEach(t=>{"dark"!==t&&void 0!==e[t]&&(s[t]=e[t])}),e.dark&&(s.dark={...t.dark},Object.keys(e.dark).forEach(t=>{void 0!==e.dark[t]&&(s.dark[t]=e.dark[t])})),s}init(){this.container.innerHTML=this.getCalendarTemplate(),this.applyGridBorders(),this.applyEventBorders(),this.applyEventColor(),this.applyViewButtonVisibility(),this.applyNavigationVisibility(),this.applyTitleVisibility(),this.applyHeaderVisibility(),this.applyCustomColors(),this.applyDetectedTheme(),this.bindEvents(),this.render()}getWeekdayDisplay(t){const e=this.options.weekdays[t];return null===this.options.showWeekdayChars?e:e.substring(0,this.options.showWeekdayChars)}triggerChangeState(){if(this.options.changeState){const t={view:this.view,date:new Date(this.currentDate),fulldayMode:this.fulldayMode,startHour:this.options.startHour,endHour:this.options.endHour,timeSlotMinutes:this.options.timeSlotMinutes};this.options.changeState(t)}}applyGridBorders(){const t=this.container.querySelector(".sc-calendar");t&&(t.classList.remove("sc-borders-both","sc-borders-vertical","sc-borders-horizontal","sc-borders-none"),t.classList.add(`sc-borders-${this.options.gridBorders}`))}applyEventBorders(){const t=this.container.querySelector(".sc-calendar");t&&(t.classList.remove("sc-event-borders","sc-event-no-borders"),this.options.eventBorders?(t.classList.add("sc-event-borders"),t.style.setProperty("--sc-event-border-color",this.options.eventBorderColor)):t.classList.add("sc-event-no-borders"))}applyEventColor(){const t=this.container.querySelector(".sc-calendar");t&&t.style.setProperty("--sc-event-color",this.options.defaultEventColor)}applyViewButtonVisibility(){const t=this.container.querySelector('[data-view="month"]'),e=this.container.querySelector('[data-view="week"]'),s=this.container.querySelector('[data-view="day"]');t&&(t.style.display=this.options.showMonthButton?"flex":"none"),e&&(e.style.display=this.options.showWeekButton?"flex":"none"),s&&(s.style.display=this.options.showDayButton?"flex":"none");const n=this.container.querySelector(".sc-view-switcher");if(n){const t=this.options.showMonthButton||this.options.showWeekButton||this.options.showDayButton;n.style.display=t?"flex":"none"}this.applyHeaderVisibility()}applyNavigationVisibility(){const t=this.container.querySelector(".sc-nav");t&&(t.style.visibility=this.options.showNavigation?"visible":"hidden"),this.applyHeaderVisibility()}applyTitleVisibility(){const t=this.container.querySelector(".sc-title-container"),e=this.container.querySelector(".sc-title-month"),s=this.container.querySelector(".sc-title-year");if(t&&(t.style.display=this.options.showTitle?"block":"none"),this.options.showTitle){e&&(e.style.display=this.options.showMonth?"inline":"none"),s&&(s.style.display=this.options.showYear?"inline":"none");const t=this.container.querySelector(".sc-title");t&&this.options.showMonth&&this.options.showYear?e&&s&&(e.style.marginRight="0.5em"):t&&e&&(e.style.marginRight="0")}this.applyHeaderVisibility()}applyHeaderVisibility(){const t=this.container.querySelector(".sc-header");if(!t)return;const e=this.options.showNavigation,s=this.options.showTitle&&(this.options.showMonth||this.options.showYear),n=this.options.showMonthButton||this.options.showWeekButton||this.options.showDayButton,i=e||s||n;t.style.display=i?"grid":"none"}applyCustomColors(){if(!this.container.querySelector(".sc-calendar"))return;const t=this.options.colors,e=(t,e)=>void 0!==e?`--${t}: ${e} !important;`:"",s=`\n .sc-calendar {\n ${e("sc-bg-primary",t.background)}\n ${e("sc-bg-secondary",t.backgroundSecondary)}\n ${e("sc-bg-tertiary",t.backgroundTertiary)}\n ${e("sc-text-primary",t.text)}\n ${e("sc-text-secondary",t.textSecondary)}\n ${e("sc-text-muted",t.textMuted)}\n ${e("sc-border-color",t.border)}\n ${e("sc-border-light",t.borderLight)}\n ${e("sc-accent-color",t.accent)}\n ${e("sc-accent-hover",t.accentHover)}\n ${e("sc-today-bg",t.todayBg)}\n ${e("sc-today-text",t.todayText)}\n ${e("sc-hover-bg",t.hoverBg)}\n }\n\n ${t.dark?`\n [data-theme="dark"] .sc-calendar {\n ${e("sc-bg-primary",t.dark.background)}\n ${e("sc-bg-secondary",t.dark.backgroundSecondary)}\n ${e("sc-bg-tertiary",t.dark.backgroundTertiary)}\n ${e("sc-text-primary",t.dark.text)}\n ${e("sc-text-secondary",t.dark.textSecondary)}\n ${e("sc-text-muted",t.dark.textMuted)}\n ${e("sc-border-color",t.dark.border)}\n ${e("sc-border-light",t.dark.borderLight)}\n ${e("sc-accent-color",t.dark.accent)}\n ${e("sc-accent-hover",t.dark.accentHover)}\n ${e("sc-today-bg",t.dark.todayBg)}\n ${e("sc-today-text",t.dark.todayText)}\n ${e("sc-hover-bg",t.dark.hoverBg)}\n }\n `:""}\n `;let n=document.getElementById("sc-custom-colors");n||(n=document.createElement("style"),n.id="sc-custom-colors",document.head.appendChild(n)),n.textContent=s}getCalendarTemplate(){return`\n <div class="sc-calendar">\n <div class="sc-header">\n <div class="sc-nav">\n <button class="sc-btn sc-prev">←</button>\n <button class="sc-btn sc-next">→</button>\n </div>\n <div class="sc-title-container">\n <h2 class="sc-title">\n <span class="sc-title-month"></span>\n <span class="sc-title-year"></span>\n </h2>\n <div class="sc-month-dropdown" style="display: none;">\n ${this.options.months.map((t,e)=>`<div class="sc-month-option" data-month="${e}">${t}</div>`).join("")}\n </div>\n <div class="sc-year-dropdown" style="display: none;">\n </div>\n </div>\n <div class="sc-view-switcher">\n <button class="sc-btn sc-view-btn ${"month"===this.view?"sc-active":""}" data-view="month">${this.options.labels.month}</button>\n <button class="sc-btn sc-view-btn ${"week"===this.view?"sc-active":""}" data-view="week">${this.options.labels.week}</button>\n <button class="sc-btn sc-view-btn ${"day"===this.view?"sc-active":""}" data-view="day">${this.options.labels.day}</button>\n </div>\n </div>\n <div class="sc-content">\n <div class="sc-view-container"></div>\n </div>\n </div>\n `}bindEvents(){const t=this.container.querySelector(".sc-prev"),e=this.container.querySelector(".sc-next"),s=this.container.querySelectorAll(".sc-view-btn");t.addEventListener("click",()=>this.navigate("prev")),e.addEventListener("click",()=>this.navigate("next")),s.forEach(t=>{t.addEventListener("click",t=>{const e=t.target.dataset.view;this.setView(e)})}),this.container.addEventListener("click",t=>{this.handleCalendarClick(t)}),this.outsideClickBound||(document.addEventListener("click",t=>{t.target.closest(".sc-title-container")||(this.hideMonthDropdown(),this.hideYearDropdown())}),this.outsideClickBound=!0)}bindMonthViewDropdowns(){if("month"!==this.view)return;const t=this.container.querySelector(".sc-title-month"),e=this.container.querySelector(".sc-title-year");if(t){const e=t.cloneNode(!0);t.parentNode.replaceChild(e,t),e.addEventListener("click",t=>{t.stopPropagation(),this.hideYearDropdown(),this.toggleMonthDropdown()})}if(e){const t=e.cloneNode(!0);e.parentNode.replaceChild(t,e),t.addEventListener("click",t=>{t.stopPropagation(),this.hideMonthDropdown(),this.toggleYearDropdown()})}this.generateYearOptions(),this.dropdownOptionsHandler&&this.container.removeEventListener("click",this.dropdownOptionsHandler),this.dropdownOptionsHandler=t=>{if(t.target.classList.contains("sc-month-option")){const e=parseInt(t.target.getAttribute("data-month"));this.setMonth(e),this.hideMonthDropdown()}else if(t.target.classList.contains("sc-year-option")){const e=parseInt(t.target.getAttribute("data-year"));this.setYear(e),this.hideYearDropdown()}},this.container.addEventListener("click",this.dropdownOptionsHandler);this.container.querySelectorAll(".sc-month-option"),this.container.querySelectorAll(".sc-year-option")}calculatePreciseTime(t,e){const s=t.getAttribute("data-time");if(!s)return null;const n=t.getBoundingClientRect(),i=e.clientY-n.top>n.height/2,a=this.parseTimeString(s);return a?(i&&a.setMinutes(a.getMinutes()+30),this.formatTime(a)):s}handleCalendarClick(t){if(t.target.classList.contains("sc-title-month")||t.target.classList.contains("sc-title-year")||t.target.classList.contains("sc-month-option")||t.target.classList.contains("sc-year-option")||t.target.closest(".sc-month-dropdown")||t.target.closest(".sc-year-dropdown"))return;const e=t.target.closest("[data-event-id]");if(e)return t.stopPropagation(),void this.handleEventClick(e,t);const s=t.target.closest("[data-date]");s&&this.handleDayClick(s,t)}handleEventClick(t,e){if(!this.options.eventClick)return;const s=t.getAttribute("data-event-id"),n=t.closest("[data-date]"),i=n?n.getAttribute("data-date"):null;if(s&&i){const t=new Date(i),a=this.getAllEventsForRange().find(t=>t.id==s);if(a){const s={date:t,time:this.calculatePreciseTime(n,e)};this.options.eventClick(a,s,e)}}}handleDayClick(t,e){if(!this.options.dayClick)return;const s=t.getAttribute("data-date");if(s){const n={date:new Date(s),time:this.calculatePreciseTime(t,e)};this.options.dayClick(n,e)}}navigate(t){const e="next"===t?1:-1;switch(this.view){case"month":this.currentDate.setMonth(this.currentDate.getMonth()+e);break;case"week":this.currentDate.setDate(this.currentDate.getDate()+7*e);break;case"day":this.currentDate.setDate(this.currentDate.getDate()+e)}this.render(),this.triggerChangeState()}toggleMonthDropdown(){if("month"!==this.view)return;const t=this.container.querySelector(".sc-month-dropdown");if(t){"none"!==t.style.display?this.hideMonthDropdown():(this.updateMonthDropdownSelection(),t.style.display="block",this.scrollToActiveMonth())}}hideMonthDropdown(){const t=this.container.querySelector(".sc-month-dropdown");t&&(t.style.display="none")}updateMonthDropdownSelection(){const t=this.container.querySelectorAll(".sc-month-option"),e=this.currentDate.getMonth();t.forEach((t,s)=>{t.classList.remove("sc-current"),s===e&&t.classList.add("sc-current")})}toggleYearDropdown(){if("month"!==this.view)return;const t=this.container.querySelector(".sc-year-dropdown");if(t){"none"!==t.style.display?this.hideYearDropdown():(this.generateYearOptions(),this.updateYearDropdownSelection(),t.style.display="block",this.scrollToActiveYear())}}hideYearDropdown(){const t=this.container.querySelector(".sc-year-dropdown");t&&(t.style.display="none")}generateYearOptions(){const t=this.container.querySelector(".sc-year-dropdown"),e=this.currentDate.getFullYear(),s=e+10;if(t){let n="";for(let t=e-10;t<=s;t++)n+=`<div class="sc-year-option" data-year="${t}">${t}</div>`;t.innerHTML=n}}updateYearDropdownSelection(){const t=this.container.querySelectorAll(".sc-year-option"),e=this.currentDate.getFullYear();t.forEach(t=>{t.classList.remove("sc-current"),parseInt(t.getAttribute("data-year"))===e&&t.classList.add("sc-current")})}setYear(t){this.currentDate.setFullYear(t),this.render(),this.triggerChangeState()}scrollToActiveMonth(){const t=this.container.querySelector(".sc-month-dropdown"),e=t?.querySelector(".sc-month-option.sc-current");t&&e&&setTimeout(()=>{t.getBoundingClientRect(),e.getBoundingClientRect(),t.scrollTop;const s=e.offsetTop-t.clientHeight/2+e.offsetHeight/2;t.scrollTop=s},0)}scrollToActiveYear(){const t=this.container.querySelector(".sc-year-dropdown"),e=t?.querySelector(".sc-year-option.sc-current");t&&e&&setTimeout(()=>{t.getBoundingClientRect(),e.getBoundingClientRect(),t.scrollTop;const s=e.offsetTop-t.clientHeight/2+e.offsetHeight/2;t.scrollTop=s},0)}setMonth(t){this.currentDate.setMonth(t),this.render(),this.triggerChangeState()}setView(t){this.view=t,this.container.querySelectorAll(".sc-view-btn").forEach(t=>{t.classList.remove("sc-active")}),this.container.querySelector(`[data-view="${t}"]`).classList.add("sc-active"),this.hideMonthDropdown(),this.hideYearDropdown(),this.render(),this.triggerChangeState()}render(){this.updateTitle(),this.renderView(),"month"===this.view&&this.bindMonthViewDropdowns()}updateTitle(){const t=this.container.querySelector(".sc-title"),e=this.container.querySelector(".sc-title-month"),s=this.container.querySelector(".sc-title-year");switch(this.view){case"month":e&&s?(e.textContent=this.options.months[this.currentDate.getMonth()],s.textContent=this.currentDate.getFullYear()):t.innerHTML=`<span class="sc-title-month">${this.options.months[this.currentDate.getMonth()]}</span> <span class="sc-title-year">${this.currentDate.getFullYear()}</span>`;break;case"week":const n=this.getWeekStart(this.currentDate),i=new Date(n);i.setDate(i.getDate()+6),t.textContent=`${this.options.months[n.getMonth()]} ${n.getDate()} - ${this.options.months[i.getMonth()]} ${i.getDate()}, ${n.getFullYear()}`;break;case"day":t.textContent=`${this.options.weekdays[this.currentDate.getDay()]}, ${this.options.months[this.currentDate.getMonth()]} ${this.currentDate.getDate()}, ${this.currentDate.getFullYear()}`}}renderView(){const t=this.container.querySelector(".sc-view-container");switch(this.view){case"month":t.innerHTML=this.renderMonthView();break;case"week":t.innerHTML=this.renderWeekView();break;case"day":t.innerHTML=this.renderDayView()}}renderMonthView(){const t=this.currentDate.getFullYear(),e=this.currentDate.getMonth(),s=new Date(t,e,1),n=new Date(s);n.setDate(n.getDate()-s.getDay());let i=`\n <div class="sc-month-view ${this.fulldayMode?"sc-fullday":""}">\n <div class="sc-weekdays">\n ${this.options.weekdays.map((t,e)=>`<div class="sc-weekday">${this.getWeekdayDisplay(e)}</div>`).join("")}\n </div>\n <div class="sc-days-grid">\n `;for(let t=0;t<6;t++)for(let s=0;s<7;s++){const a=new Date(n);a.setDate(n.getDate()+7*t+s);const o=a.getMonth()===e,r=this.isToday(a),c=this.getEventsForDate(a);if(this.fulldayMode){const t=this.addEventPlaceholders(c,a);i+=`\n <div class="sc-day ${o?"sc-current-month":"sc-other-month"} ${r?"sc-today":""}" data-date="${a.toISOString()}">\n <div class="sc-day-number">${a.getDate()}</div>\n ${t.map(t=>{if(t.isPlaceholder)return'<div class="sc-event-month sc-event-single sc-event-placeholder">&nbsp;</div>';const e=this.getEventPosition(t.event,a),s=e.isSingle?"sc-event-single":e.isStart?"sc-event-start":e.isEnd?"sc-event-end":"sc-event-middle",n=e.isSingle||e.isStart?t.event.title:"",i=this.applyEventStyles(t.event);return`<div class="sc-event-month ${s}" data-event-id="${t.event.id}" ${i}><span class="sc-event-text">${n}</span></div>`}).join("")}\n </div>\n `}else{const t=c.length,e=0===t?"":1===t?`1 ${this.options.labels.event}`:`${t} ${this.options.labels.events}`;i+=`\n <div class="sc-day sc-day-count-mode ${o?"sc-current-month":"sc-other-month"} ${r?"sc-today":""}" data-date="${a.toISOString()}">\n <div class="sc-day-content">\n <div class="sc-day-number">${a.getDate()}</div>\n ${e?`<div class="sc-event-count">${e}</div>`:""}\n </div>\n </div>\n `}}return i+="\n </div>\n </div>\n ",i}renderWeekView(){const t=this.getWeekStart(this.currentDate),e=[];for(let s=0;s<7;s++){const n=new Date(t);n.setDate(n.getDate()+s),e.push(n)}if(this.fulldayMode)return this.renderWeekFullday(e);const s=this.generateTimeSlots();return`\n <div class="sc-week-view">\n <div class="sc-time-column-header"></div>\n ${e.map(t=>`\n <div class="sc-day-header ${this.isToday(t)?"sc-today":""}">\n <div class="sc-day-name">${this.getWeekdayDisplay(t.getDay())}</div>\n <div class="sc-day-number">${t.getDate()}</div>\n </div>\n `).join("")}\n ${s.map(t=>`\n <div class="sc-time-slot">${t}</div>\n ${e.map(e=>{const s=this.getEventsForTimeSlot(e,t);return`\n <div class="sc-day-column ${this.isToday(e)?"sc-today":""}" data-date="${e.toISOString()}" data-time="${t}">\n <div class="sc-half-hour-line"></div>\n ${s.map(t=>{const e=this.applyEventStyles(t);return`<div class="sc-event-time" data-event-id="${t.id}" ${e}>${t.title}</div>`}).join("")}\n </div>\n `}).join("")}\n `).join("")}\n </div>\n `}renderWeekFullday(t){return`\n <div class="sc-week-view sc-fullday">\n <div class="sc-weekdays">\n ${t.map(t=>`<div class="sc-weekday">${this.getWeekdayDisplay(t.getDay())}</div>`).join("")}\n </div>\n <div class="sc-days-grid">\n ${t.map(t=>{const e=this.isToday(t),s=this.getEventsForDate(t),n=this.addEventPlaceholders(s,t);return`\n <div class="sc-day ${e?"sc-today":""}" data-date="${t.toISOString()}">\n <div class="sc-day-number">${t.getDate()}</div>\n ${n.map(e=>{if(e.isPlaceholder)return'<div class="sc-event-week sc-event-single sc-event-placeholder">&nbsp;</div>';const s=this.getEventPosition(e.event,t),n=s.isSingle?"sc-event-single":s.isStart?"sc-event-start":s.isEnd?"sc-event-end":"sc-event-middle",i=s.isSingle||s.isStart?e.event.title:"",a=this.applyEventStyles(e.event);return`<div class="sc-event-week ${n}" data-event-id="${e.event.id}" ${a}><span class="sc-event-text">${i}</span></div>`}).join("")}\n </div>\n `}).join("")}\n </div>\n </div>\n `}renderDayView(){const t=new Date(this.currentDate);if(this.fulldayMode){const e=this.getEventsForDate(t);return`\n <div class="sc-day-view sc-fullday">\n <div class="sc-day-header ${this.isToday(t)?"sc-today":""}">\n <div class="sc-day-name">${this.options.weekdays[t.getDay()]}</div>\n <div class="sc-day-number">${t.getDate()}</div>\n </div>\n <div class="sc-day-events" data-date="${t.toISOString()}">\n ${e.map(e=>{const s=this.getEventPosition(e,t),n=s.isSingle?"sc-event-single":s.isStart?"sc-event-start":s.isEnd?"sc-event-end":"sc-event-middle",i=e.title,a=this.applyEventStyles(e);return`<div class="sc-event-day ${n}" data-event-id="${e.id}" ${a}><span class="sc-event-text">${i}</span></div>`}).join("")}\n </div>\n </div>\n `}const e=this.generateTimeSlots();return`\n <div class="sc-day-view">\n <div class="sc-time-column-header"></div>\n <div class="sc-day-header ${this.isToday(t)?"sc-today":""}">\n <div class="sc-day-name">${this.options.weekdays[t.getDay()]}</div>\n <div class="sc-day-number">${t.getDate()}</div>\n </div>\n ${e.map(e=>{const s=this.getEventsForTimeSlot(t,e);return`\n <div class="sc-time-slot">${e}</div>\n <div class="sc-day-column ${this.isToday(t)?"sc-today":""}" data-date="${t.toISOString()}" data-time="${e}">\n <div class="sc-half-hour-line"></div>\n ${s.map(t=>{const e=this.applyEventStyles(t);return`<div class="sc-event-time" data-event-id="${t.id}" ${e}>${t.title}</div>`}).join("")}\n </div>\n `}).join("")}\n </div>\n `}generateTimeSlots(){const t=[],e=this.options.startHour,s=this.options.endHour,n=new Date;n.setHours(e,0,0,0),t.push(`${this.options.labels.before} ${this.formatTime(n)}`);for(let n=e;n<=s;n++){const e=new Date;e.setHours(n,0,0,0),t.push(this.formatTime(e))}const i=new Date;return i.setHours(s,0,0,0),t.push(`${this.options.labels.after} ${this.formatTime(i)}`),t}formatTime(t){return t.toLocaleTimeString("en-US",{hour:"numeric",minute:"2-digit",hour12:!0})}getWeekStart(t){const e=new Date(t);return e.setDate(e.getDate()-e.getDay()),e}getVisibleDateRange(){const t=this.currentDate.getFullYear(),e=this.currentDate.getMonth();switch(this.view){case"month":const s=new Date(t,e,1),n=new Date(s);n.setDate(n.getDate()-s.getDay());const i=new Date(n);return i.setDate(i.getDate()+41),{startDate:n,endDate:i};case"week":const a=this.getWeekStart(this.currentDate),o=new Date(a);return o.setDate(o.getDate()+6),{startDate:a,endDate:o};case"day":const r=new Date(this.currentDate);r.setHours(0,0,0,0);const c=new Date(r);return c.setHours(23,59,59,999),{startDate:r,endDate:c}}}isToday(t){const e=new Date;return t.toDateString()===e.toDateString()}isRangeCovered(t,e){for(const s of this.fetchedRanges)if(s.startDate<=t&&s.endDate>=e)return!0;return!1}getMissingRanges(t,e){const s=[];let n=new Date(t);const i=[...this.fetchedRanges].sort((t,e)=>t.startDate-e.startDate);for(const t of i){if(n<t.startDate&&n<=e){const i=new Date(Math.min(t.startDate.getTime()-864e5,e.getTime()));i>=n&&s.push({startDate:new Date(n),endDate:i})}t.endDate>=n&&(n=new Date(t.endDate.getTime()+864e5))}return n<=e&&s.push({startDate:new Date(n),endDate:new Date(e)}),s}addFetchedRange(t,e,s){const n={startDate:new Date(t),endDate:new Date(e),events:s};this.allCachedEvents=this.allCachedEvents.concat(s),this.fetchedRanges.push(n),this.mergeOverlappingRanges()}mergeOverlappingRanges(){this.fetchedRanges.sort((t,e)=>t.startDate-e.startDate);const t=[];for(const e of this.fetchedRanges)if(0===t.length||t[t.length-1].endDate<e.startDate)t.push(e);else{const s=t[t.length-1];s.endDate=new Date(Math.max(s.endDate.getTime(),e.endDate.getTime())),s.events=s.events.concat(e.events)}this.fetchedRanges=t,this.allCachedEvents=[];for(const t of this.fetchedRanges)this.allCachedEvents=this.allCachedEvents.concat(t.events)}getAllEventsForRange(){if(this.eventsCallback){const t=this.getVisibleDateRange();if(this.isRangeCovered(t.startDate,t.endDate))return this.allCachedEvents;const e=this.getMissingRanges(t.startDate,t.endDate);for(const t of e){const e=this.eventsCallback(t);this.addFetchedRange(t.startDate,t.endDate,e)}return this.allCachedEvents}return this.events||[]}getEventsForDate(t){return this.getAllEventsForRange().filter(e=>{const s=new Date(e.startDate||e.date),n=new Date(e.endDate||e.date),i=new Date(t);return i.setHours(0,0,0,0),s.setHours(0,0,0,0),n.setHours(0,0,0,0),i>=s&&i<=n}).sort((t,e)=>{const s=this.isMultiDayEvent(t),n=this.isMultiDayEvent(e);return s&&!n?-1:!s&&n?1:0})}getEventsForTimeSlot(t,e){if(this.fulldayMode)return this.getEventsForDate(t);return this.getEventsForDate(t).filter(t=>{if(!t.time)return!1;const s=this.parseTimeString(t.time);if(!s)return!1;const n=s.getHours(),i=this.options.startHour,a=this.options.endHour;if(e.startsWith(this.options.labels.before))return n<i;if(e.startsWith(this.options.labels.after))return n>a;const o=this.parseTimeString(e);if(!o)return!1;return n===o.getHours()})}parseTimeString(t){try{const e=new Date,[s,n]=t.split(" "),[i,a]=s.split(":").map(Number);let o=i;return"PM"===n&&12!==i&&(o+=12),"AM"===n&&12===i&&(o=0),e.setHours(o,a||0,0,0),e}catch(t){return null}}isMultiDayEvent(t){const e=new Date(t.startDate||t.date),s=new Date(t.endDate||t.date);return e.setHours(0,0,0,0),s.setHours(0,0,0,0),e.getTime()!==s.getTime()}addEventPlaceholders(t,e){if(!this.fulldayMode)return t.map(t=>({event:t,isPlaceholder:!1}));const s=this.getWeekStart(e),n=[];for(let t=0;t<7;t++){const e=new Date(s);e.setDate(e.getDate()+t),n.push(e)}const i=new Map;n.forEach(t=>{this.getEventsForDate(t).filter(t=>this.isMultiDayEvent(t)).forEach(t=>{i.has(t.id)||i.set(t.id,t)})});const a=Array.from(i.values()).sort((t,e)=>new Date(t.startDate||t.date)-new Date(e.startDate||e.date)),o=[],r=t.filter(t=>this.isMultiDayEvent(t)),c=t.filter(t=>!this.isMultiDayEvent(t));return a.forEach(t=>{const n=r.find(e=>e.id===t.id);if(n)o.push({event:n,isPlaceholder:!1});else{const n=new Date(t.startDate||t.date),i=new Date(t.endDate||t.date);n.setHours(0,0,0,0),i.setHours(0,0,0,0);const a=new Date(e);a.setHours(0,0,0,0);const r=new Date(s);r.setHours(0,0,0,0);const c=new Date(s);c.setDate(c.getDate()+6),c.setHours(0,0,0,0);n<=c&&(i>=r&&i<=c)&&a>i&&o.push({isPlaceholder:!0,eventId:t.id})}}),c.forEach(t=>{o.push({event:t,isPlaceholder:!1})}),o}getEventPosition(t,e){const s=new Date(t.startDate||t.date),n=new Date(t.endDate||t.date),i=new Date(e);s.setHours(0,0,0,0),n.setHours(0,0,0,0),i.setHours(0,0,0,0);return{isStart:i.getTime()===s.getTime(),isEnd:i.getTime()===n.getTime(),isMiddle:i>s&&i<n,isSingle:s.getTime()===n.getTime()}}addEvent(t){this.eventsCallback||(this.events.push(t),this.render())}removeEvent(t){this.eventsCallback||(this.events=this.events.filter(e=>e.id!==t),this.render())}setEvents(t){"function"==typeof t?(this.eventsCallback=t,this.events=[]):(this.events=t||[],this.eventsCallback=null),this.fetchedRanges=[],this.allCachedEvents=[],this.render()}setFulldayMode(t){this.fulldayMode=t,this.render(),this.triggerChangeState()}goToDate(t){this.currentDate=new Date(t),this.render(),this.triggerChangeState()}goToToday(){this.currentDate=new Date,this.render(),this.triggerChangeState()}setGridBorders(t){this.options.gridBorders=t,this.applyGridBorders()}setEventBorders(t){this.options.eventBorders=t,this.applyEventBorders(),this.render()}setEventBorderColor(t){this.options.eventBorderColor=t,this.applyEventBorders(),this.render()}setShowMonthButton(t){this.options.showMonthButton=t,this.applyViewButtonVisibility()}setShowWeekButton(t){this.options.showWeekButton=t,this.applyViewButtonVisibility()}setShowDayButton(t){this.options.showDayButton=t,this.applyViewButtonVisibility()}setViewButtonsVisibility(t,e,s){this.options.showMonthButton=t,this.options.showWeekButton=e,this.options.showDayButton=s,this.applyViewButtonVisibility()}setShowNavigation(t){this.options.showNavigation=t,this.applyNavigationVisibility()}setShowTitle(t){this.options.showTitle=t,this.applyTitleVisibility()}setShowMonth(t){this.options.showMonth=t,this.applyTitleVisibility()}setShowYear(t){this.options.showYear=t,this.applyTitleVisibility()}setTitleVisibility(t,e=!0,s=!0){this.options.showTitle=t,this.options.showMonth=e,this.options.showYear=s,this.applyTitleVisibility()}setDefaultEventColor(t){this.options.defaultEventColor=t,this.applyEventColor(),this.render()}getEventBaseCssStyle(t){const e=t.color,s=!0===t.colorIsGradient,n=e?t.color:this.options.defaultEventColor;if(!n)return{};const i=(" "+n).slice(1),a=this.hexToRgb(i);if(!a)return{};const o=this.getOptimalTextColor(a);if(s){const t={r:Math.min(255,a.r+40),g:Math.min(255,a.g+40),b:Math.min(255,a.b+40)};return{"background-image":`linear-gradient(to bottom, ${this.rgbToHex(t)} 0px, ${n} 100%)`,"background-repeat":"repeat-x",color:o,"background-color":n}}return{"background-color":n,color:o}}getOptimalTextColor(t){return this.getLuminance(t)>.5?"#000000":"#ffffff"}getLuminance(t){return.2126*this.getRelativeLuminanceComponent(t.r/255)+.7152*this.getRelativeLuminanceComponent(t.g/255)+.0722*this.getRelativeLuminanceComponent(t.b/255)}getRelativeLuminanceComponent(t){return t<=.03928?t/12.92:Math.pow((t+.055)/1.055,2.4)}getContrastRatio(t,e){const s=this.getLuminance(t),n=this.getLuminance(e);return(Math.max(s,n)+.05)/(Math.min(s,n)+.05)}hexToRgb(t){const e=/^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(t);return e?{r:parseInt(e[1],16),g:parseInt(e[2],16),b:parseInt(e[3],16)}:null}rgbToHex(t){return"#"+(1<<24|t.r<<16|t.g<<8|t.b).toString(16).slice(1)}applyEventStyles(t){const e=this.getEventBaseCssStyle(t),s=Object.entries(e).map(([t,e])=>`${t}: ${e}`).join("; ");return s?`style="${s}"`:""}destroy(){this.themeObserver&&(this.themeObserver.disconnect(),this.themeObserver=null),this.systemThemeListener&&window.matchMedia&&(window.matchMedia("(prefers-color-scheme: dark)").removeEventListener("change",this.systemThemeListener),this.systemThemeListener=null),this.container&&(this.container.innerHTML="")}}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "simple-calendar-js",
3
- "version": "2.0.2",
3
+ "version": "2.0.3",
4
4
  "description": "A clean, modern, and feature-rich JavaScript calendar component with zero dependencies. Responsive design and intuitive navigation.",
5
5
  "main": "dist/SimpleCalendarJs.min.js",
6
6
  "style": "dist/SimpleCalendarJs.min.css",