simple-calendar-js 3.0.4 → 3.0.6

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/CHANGELOG.md ADDED
@@ -0,0 +1,118 @@
1
+ # Changelog
2
+
3
+ All notable changes to SimpleCalendarJs will be documented in this file.
4
+
5
+ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6
+ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
+
8
+ ## [Unreleased]
9
+
10
+ No unreleased changes yet.
11
+
12
+ ## [3.0.6] - 2026-02-24
13
+
14
+ ### Added
15
+ - Drag and drop support for moving events between dates and times (`enableDragDrop` option)
16
+ - Event resize functionality to change event duration (`enableResize` option)
17
+ - Month view timed event display style option (`monthTimedEventStyle`: `'list'` or `'block'`)
18
+ - `onEventDrop` callback for handling both move and resize operations
19
+ - Touch support for drag and drop on mobile/tablet devices
20
+ - Visual feedback during drag operations with 15-minute snap-to-grid in week/day views
21
+ - Cross-boundary conversion (timed ↔ all-day events when dragging between sections)
22
+ - Auto-calculate feature for `maxEventsPerCell`: set to `-1` to automatically calculate how many events fit based on cell height
23
+
24
+ ### Changed
25
+ - **`maxEventsPerCell` default changed from `3` to `-1`** (auto-calculate mode)
26
+ - `-1` = Auto-calculate based on cell height (responsive, adapts to calendar size)
27
+ - `0` = Show all events, cells expand infinitely
28
+ - `1+` = Show exactly that number, rest in "+N more"
29
+ - Week, day, and list views now have a max-height of 650px and scroll content while keeping headers and toolbar fixed in place
30
+ - Month view maintains its natural expanding height based on number of weeks and events per cell
31
+ - All scrollable views use consistent custom scrollbar styling
32
+ - Calendar adapts to container height: if you set a custom height on the container div, week/day/list views will respect it
33
+ - Week/day view all-day events now always display titles on continuation segments (matches day view behavior)
34
+
35
+ ### Fixed
36
+ - List view sticky date headers now have opaque background to prevent events showing through when scrolling
37
+ - Calendar no longer rebuilds entire shell during event fetching, preventing layout shift and page content jumping
38
+
39
+ ## [3.0.5] - 2026-02-23
40
+
41
+ No core calendar changes in this version.
42
+
43
+ ## [3.0.4] - 2026-02-23
44
+
45
+ ### Added
46
+ - List view mode for chronological event display
47
+ - Intelligent event caching system to minimize network requests
48
+ - Localized "All-day" text display using Intl API
49
+
50
+ ### Fixed
51
+ - Border-radius consistency across all event types
52
+ - CSS styling improvements for event rendering
53
+
54
+ ## [3.0.3] - 2026-02-22
55
+
56
+ ### Added
57
+ - `maxEventsPerCell` option to limit events displayed per cell in month view (with "+N more" indicator)
58
+ - Set to `0` for unlimited events display
59
+
60
+ ### Fixed
61
+ - Date navigation edge cases when switching between views
62
+
63
+ ## [3.0.2] - 2026-02-20
64
+
65
+ ### Added
66
+ - `showBorder` option to control calendar outer border visibility
67
+ - `weekdayFormat` option with three formats: `'narrow'` (1-2 letters), `'short'` (abbreviated), `'long'` (full name)
68
+
69
+ ### Changed
70
+ - Improved theming system with better CSS custom property organization
71
+
72
+ ## [3.0.1] - 2026-02-19
73
+
74
+ ### Added
75
+ - Event tooltips with smart positioning system
76
+ - Tooltip configuration with `showTooltips` option
77
+ - Multi-line tooltip support using `\n` newline characters
78
+ - Customizable tooltip styling via CSS custom properties
79
+
80
+ ### Changed
81
+ - Optimized overflow behavior for better event visibility
82
+
83
+ ## [3.0.0] - 2026-02-19
84
+
85
+ ### Breaking Changes
86
+ **Version 3.0.0 represents a major rewrite with significant breaking changes from version 2.x**
87
+
88
+ This version is not backward compatible with 2.x. Please review all changes before upgrading.
89
+
90
+ ### Added
91
+ - **Framework Wrappers**: Official React, Vue 3, and Angular component wrappers
92
+ - **Four View Modes**: Month, week, day, and list views with smooth transitions
93
+ - **Full Internationalization**: Built on `Intl.DateTimeFormat` API for native locale support
94
+ - **Dark Mode Support**: Automatic dark theme detection and manual toggle support
95
+ - **Month and Year Dropdowns**: Interactive dropdowns for quick date navigation
96
+ - **Year Picker**: Quick year selection in month view
97
+ - **Color System**: Comprehensive theming with CSS custom properties
98
+ - **Global Export**: Proper UMD module support (CommonJS, AMD, ES modules, browser global)
99
+ - **Accessibility**: Semantic HTML with proper ARIA attributes
100
+
101
+ ### Changed
102
+ - **Standardized file naming**: Renamed and reorganized source files for consistency
103
+ - **Improved alignment**: Better container and element alignment across all views
104
+ - **Font inheritance**: Calendar now inherits font from parent container
105
+ - **Updated license and headers**: Clarified licensing for commercial vs non-commercial use
106
+ - **Complete UI overhaul**: Redesigned interface with modern styling
107
+
108
+ ### Fixed
109
+ - Hour and half-hour grid lines now properly visible in day/week views
110
+ - GitHub Actions workflow configuration
111
+
112
+ ---
113
+
114
+ ## Version 2.x and Earlier
115
+
116
+ Changes prior to version 3.0.0 are not documented in this changelog as version 3.0.0 represents a complete rewrite of the library.
117
+
118
+ For reference, version 2.0.1 was the last release in the 2.x series.
package/README.md CHANGED
@@ -2,8 +2,8 @@
2
2
 
3
3
  A lightweight, zero-dependency JavaScript calendar component with internationalization support and framework wrappers for React, Vue, and Angular.
4
4
 
5
- ![Version](https://img.shields.io/badge/version-3.0.4-blue.svg)
6
- ![License](https://img.shields.io/badge/license-See%20LICENSE-green.svg)
5
+ [![npm version](https://img.shields.io/npm/v/simple-calendar-js)](https://www.npmjs.com/package/simple-calendar-js)
6
+ [![license](https://img.shields.io/npm/l/simple-calendar-js)](LICENSE)
7
7
 
8
8
  ## Features
9
9
 
@@ -17,7 +17,7 @@ A lightweight, zero-dependency JavaScript calendar component with internationali
17
17
  - **Responsive Design** - Adapts to any screen size
18
18
  - **Customizable Styling** - CSS custom properties for easy theming
19
19
  - **Accessible** - Semantic HTML with proper ARIA attributes
20
- - **Small Bundle Size** - ~22KB minified JS, ~14KB minified CSS
20
+ - **Small Bundle Size** - ~42KB minified JS + ~20KB CSS (~15KB total gzipped)
21
21
 
22
22
  ## Installation
23
23
 
@@ -168,7 +168,7 @@ export class CalendarComponent {
168
168
  | `showTimeInItems` | boolean | `true` | Show time in event items |
169
169
  | `showGridLines` | boolean | `true` | Show calendar grid lines |
170
170
  | `showBorder` | boolean | `true` | Show calendar outer border |
171
- | `maxEventsPerCell` | number | `3` | Maximum events per cell in month view before showing "+N more". Set to `0` for unlimited (show all events) |
171
+ | `maxEventsPerCell` | number | `-1` | Maximum events per cell in month view. `-1` = auto-calculate based on cell height (responsive), `0` = unlimited (show all events), `1+` = show exactly that number |
172
172
  | `showToolbar` | boolean | `true` | Show the toolbar |
173
173
  | `showTodayButton` | boolean | `true` | Show "Today" button |
174
174
  | `showNavigation` | boolean | `true` | Show prev/next navigation arrows |
@@ -178,11 +178,15 @@ export class CalendarComponent {
178
178
  | `showTooltips` | boolean | `true` | Show tooltips on hover for events |
179
179
  | `listDaysForward` | number | `30` | Number of days forward to show in list view |
180
180
  | `enabledViews` | string[] | `['month', 'week', 'day']` | Available view modes. Add `'list'` to enable list view |
181
+ | `enableDragDrop` | boolean | `false` | Enable drag and drop to move events |
182
+ | `enableResize` | boolean | `false` | Enable resizing events to change duration |
183
+ | `monthTimedEventStyle` | string | `'list'` | Display style for timed events in month view: `'list'` (schedule format) or `'block'` (traditional blocks) |
181
184
  | `fetchEvents` | function | `null` | Async function to fetch events: `async (start, end) => Event[]` |
182
185
  | `onEventClick` | function | `null` | Callback when event is clicked: `(event, mouseEvent) => void` |
183
186
  | `onSlotClick` | function | `null` | Callback when time slot is clicked: `(date, mouseEvent) => void` |
184
187
  | `onViewChange` | function | `null` | Callback when view changes: `(view) => void` |
185
188
  | `onNavigate` | function | `null` | Callback when date range changes: `(startDate, endDate) => void` |
189
+ | `onEventDrop` | function | `null` | Callback when event is dropped: `(event, oldStart, oldEnd, newStart, newEnd) => void` |
186
190
 
187
191
  ## Event Object Format
188
192
 
@@ -414,6 +418,142 @@ Tooltips use CSS custom properties and can be customized:
414
418
  - Special characters are automatically escaped for security
415
419
  - Maximum width is 250px by default (can be customized via CSS variables)
416
420
 
421
+ ## Drag and Drop
422
+
423
+ SimpleCalendarJs supports drag and drop for moving events and resizing them to change their duration.
424
+
425
+ ### Configuration
426
+
427
+ ```javascript
428
+ const calendar = new SimpleCalendarJs('#calendar', {
429
+ enableDragDrop: true, // Enable moving events
430
+ enableResize: true, // Enable resizing events
431
+
432
+ onEventDrop: (event, oldStart, oldEnd, newStart, newEnd) => {
433
+ // Detect if this is a move or resize
434
+ const isMoved = oldStart.getTime() !== newStart.getTime();
435
+ const isResized = oldEnd.getTime() !== newEnd.getTime() && !isMoved;
436
+
437
+ if (isMoved) {
438
+ console.log(`Event "${event.title}" moved to ${newStart}`);
439
+ } else if (isResized) {
440
+ console.log(`Event "${event.title}" resized to end at ${newEnd}`);
441
+ }
442
+
443
+ // Update your backend
444
+ await fetch(`/api/events/${event.id}`, {
445
+ method: 'PATCH',
446
+ headers: { 'Content-Type': 'application/json' },
447
+ body: JSON.stringify({
448
+ start: newStart.toISOString(),
449
+ end: newEnd.toISOString(),
450
+ allDay: event.allDay
451
+ })
452
+ });
453
+ }
454
+ });
455
+ ```
456
+
457
+ ### Moving Events (`enableDragDrop`)
458
+
459
+ **How It Works:**
460
+ - **Drag Initiation**: Click and hold on an event, then move at least 5px or wait 150ms
461
+ - **Visual Feedback**: The event follows your cursor while dragging
462
+ - **Snap to Grid**: Events snap to 15-minute intervals in week/day views
463
+ - **Drop**: Release to drop the event at the new date/time
464
+ - **Cancel**: Press ESC to cancel the drag operation
465
+ - **Duration Preservation**: Events maintain their duration when moved
466
+ - **Touch Support**: Full support for mobile/tablet touch gestures
467
+
468
+ **Cross-Boundary Conversion:**
469
+
470
+ When dragging events between different sections:
471
+
472
+ **Timed Event → All-Day Section (Week/Day Views):**
473
+ - Converts to an all-day event
474
+ - Preserves the day span
475
+
476
+ **All-Day Event → Timed Section (Week/Day Views):**
477
+ - Converts to a timed event
478
+ - Default duration: 1 hour
479
+ - Snaps to the time slot where dropped
480
+
481
+ **Month View:**
482
+ - Events maintain their original type (all-day stays all-day, timed stays timed)
483
+ - Timed events preserve their original time of day
484
+
485
+ ### Resizing Events (`enableResize`)
486
+
487
+ **Horizontal Resize (All-Day Events):**
488
+ - **Visual Indicator**: Small vertical line appears on the right edge when hovering
489
+ - **How It Works**: Drag the right edge to change the number of days the event spans
490
+ - **Available In**: Month view, week/day all-day sections
491
+ - **Minimum**: 1 day
492
+
493
+ **Vertical Resize (Timed Events):**
494
+ - **Visual Indicator**: Small horizontal line appears at the bottom when hovering
495
+ - **How It Works**: Drag the bottom edge to change the end time
496
+ - **Available In**: Week/day timed sections
497
+ - **Snap to Grid**: 15-minute intervals
498
+ - **Minimum**: 15 minutes
499
+ - **Live Feedback**: Time display updates to show start and end times as you drag
500
+
501
+ ### Callback Parameters
502
+
503
+ The `onEventDrop` callback receives the same parameters for both move and resize operations:
504
+
505
+ | Parameter | Type | Description |
506
+ |-----------|------|-------------|
507
+ | `event` | Object | The updated event object (with new start/end) |
508
+ | `oldStart` | Date | Original start date/time |
509
+ | `oldEnd` | Date | Original end date/time |
510
+ | `newStart` | Date | New start date/time |
511
+ | `newEnd` | Date | New end date/time |
512
+
513
+ **Detecting Operation Type:**
514
+ - **Move**: `oldStart !== newStart`
515
+ - **Resize**: `oldStart === newStart` and `oldEnd !== newEnd`
516
+
517
+ ### Important Notes
518
+
519
+ - The calendar updates the event internally before firing the callback
520
+ - You **must** update your backend in the `onEventDrop` callback
521
+ - If the backend update fails, call `calendar.refresh()` to revert to the previous state
522
+ - Both features are disabled in list view (read-only)
523
+ - You can enable one, both, or neither feature independently
524
+
525
+ ## Month View Timed Event Display Style
526
+
527
+ The `monthTimedEventStyle` option controls how timed events are displayed in month view:
528
+
529
+ ### List Style (Default: `'list'`)
530
+ Schedule-style display with horizontal layout:
531
+ - **Colored dot**: Shows event color as a small circle
532
+ - **Time**: Displays start time (if `showTimeInItems` is enabled)
533
+ - **Title**: Event title truncated with ellipsis if too long
534
+ - **Compact**: Clean, minimal appearance similar to schedule apps
535
+
536
+ ```javascript
537
+ const calendar = new SimpleCalendarJs('#calendar', {
538
+ monthTimedEventStyle: 'list', // Default
539
+ showTimeInItems: true // Shows time next to dot
540
+ });
541
+ ```
542
+
543
+ ### Block Style (`'block'`)
544
+ Traditional calendar block display:
545
+ - **Colored Background**: Full event background in event color
546
+ - **Time Display**: Start time shown inside block (if enabled)
547
+ - **Classic Look**: Traditional calendar appearance
548
+
549
+ ```javascript
550
+ const calendar = new SimpleCalendarJs('#calendar', {
551
+ monthTimedEventStyle: 'block'
552
+ });
553
+ ```
554
+
555
+ **Note**: This option only affects timed events in month view. All-day events always display as blocks, and week/day views always use block style with duration-based heights.
556
+
417
557
  ## API Methods
418
558
 
419
559
  ```javascript
@@ -480,6 +620,190 @@ addEventToDatabase(newEvent);
480
620
  calendar.refresh(); // ✓ Fetch (clears cache)
481
621
  ```
482
622
 
623
+ ## Persisting User Preferences
624
+
625
+ The calendar doesn't include built-in persistence - this is intentionally left to your application so you have full control over how and where preferences are stored. Here are common patterns:
626
+
627
+ ### Using LocalStorage (Persistent Across Sessions)
628
+
629
+ ```javascript
630
+ // Restore saved preferences or use defaults
631
+ const savedView = localStorage.getItem('calendarView') || 'month';
632
+ const savedDate = localStorage.getItem('calendarDate');
633
+
634
+ const calendar = new SimpleCalendarJs('#calendar', {
635
+ defaultView: savedView,
636
+ defaultDate: savedDate ? new Date(savedDate) : null,
637
+
638
+ // Save view changes
639
+ onViewChange: (view) => {
640
+ localStorage.setItem('calendarView', view);
641
+ },
642
+
643
+ // Save navigation (current date)
644
+ onNavigate: (startDate) => {
645
+ localStorage.setItem('calendarDate', startDate.toISOString());
646
+ }
647
+ });
648
+ ```
649
+
650
+ ### Using SessionStorage (Per-Tab)
651
+
652
+ ```javascript
653
+ // Same as localStorage but survives only for the current tab/session
654
+ const calendar = new SimpleCalendarJs('#calendar', {
655
+ defaultView: sessionStorage.getItem('calendarView') || 'month',
656
+ onViewChange: (view) => sessionStorage.setItem('calendarView', view),
657
+ onNavigate: (start) => sessionStorage.setItem('calendarDate', start.toISOString())
658
+ });
659
+ ```
660
+
661
+ ### Using URL Query Parameters (Shareable State)
662
+
663
+ ```javascript
664
+ // Read from URL
665
+ const params = new URLSearchParams(window.location.search);
666
+ const view = params.get('view') || 'month';
667
+ const date = params.get('date') ? new Date(params.get('date')) : null;
668
+
669
+ const calendar = new SimpleCalendarJs('#calendar', {
670
+ defaultView: view,
671
+ defaultDate: date,
672
+
673
+ onViewChange: (newView) => {
674
+ const url = new URL(window.location);
675
+ url.searchParams.set('view', newView);
676
+ window.history.pushState({}, '', url);
677
+ },
678
+
679
+ onNavigate: (startDate) => {
680
+ const url = new URL(window.location);
681
+ url.searchParams.set('date', startDate.toISOString().split('T')[0]);
682
+ window.history.pushState({}, '', url);
683
+ }
684
+ });
685
+ ```
686
+
687
+ ### React Example with State Hook
688
+
689
+ ```javascript
690
+ function MyCalendar() {
691
+ const [view, setView] = useState(
692
+ () => localStorage.getItem('calendarView') || 'month'
693
+ );
694
+
695
+ const [currentDate, setCurrentDate] = useState(
696
+ () => {
697
+ const saved = localStorage.getItem('calendarDate');
698
+ return saved ? new Date(saved) : new Date();
699
+ }
700
+ );
701
+
702
+ const handleViewChange = (newView) => {
703
+ setView(newView);
704
+ localStorage.setItem('calendarView', newView);
705
+ };
706
+
707
+ const handleNavigate = (startDate) => {
708
+ setCurrentDate(startDate);
709
+ localStorage.setItem('calendarDate', startDate.toISOString());
710
+ };
711
+
712
+ return (
713
+ <SimpleCalendarJsReact
714
+ defaultView={view}
715
+ defaultDate={currentDate}
716
+ onViewChange={handleViewChange}
717
+ onNavigate={handleNavigate}
718
+ fetchEvents={fetchEvents}
719
+ />
720
+ );
721
+ }
722
+ ```
723
+
724
+ ### Vue Example with Composable
725
+
726
+ ```javascript
727
+ // composables/useCalendarPreferences.js
728
+ import { ref, watch } from 'vue';
729
+
730
+ export function useCalendarPreferences() {
731
+ const view = ref(localStorage.getItem('calendarView') || 'month');
732
+ const currentDate = ref(
733
+ localStorage.getItem('calendarDate')
734
+ ? new Date(localStorage.getItem('calendarDate'))
735
+ : new Date()
736
+ );
737
+
738
+ watch(view, (newView) => {
739
+ localStorage.setItem('calendarView', newView);
740
+ });
741
+
742
+ watch(currentDate, (newDate) => {
743
+ localStorage.setItem('calendarDate', newDate.toISOString());
744
+ });
745
+
746
+ return { view, currentDate };
747
+ }
748
+
749
+ // In component
750
+ <script setup>
751
+ import { useCalendarPreferences } from './composables/useCalendarPreferences';
752
+ const { view, currentDate } = useCalendarPreferences();
753
+
754
+ const handleViewChange = (newView) => {
755
+ view.value = newView;
756
+ };
757
+
758
+ const handleNavigate = (startDate) => {
759
+ currentDate.value = startDate;
760
+ };
761
+ </script>
762
+
763
+ <template>
764
+ <SimpleCalendarJsVue
765
+ :defaultView="view"
766
+ :defaultDate="currentDate"
767
+ @viewChange="handleViewChange"
768
+ @navigate="handleNavigate"
769
+ />
770
+ </template>
771
+ ```
772
+
773
+ ### Server-Side Preferences (Synced Across Devices)
774
+
775
+ ```javascript
776
+ // Load preferences from your API
777
+ const preferences = await fetch('/api/user/calendar-preferences').then(r => r.json());
778
+
779
+ const calendar = new SimpleCalendarJs('#calendar', {
780
+ defaultView: preferences.view || 'month',
781
+ defaultDate: preferences.lastViewedDate ? new Date(preferences.lastViewedDate) : null,
782
+
783
+ onViewChange: async (view) => {
784
+ // Debounce to avoid too many API calls
785
+ clearTimeout(window.savePreferencesTimeout);
786
+ window.savePreferencesTimeout = setTimeout(() => {
787
+ fetch('/api/user/calendar-preferences', {
788
+ method: 'PATCH',
789
+ headers: { 'Content-Type': 'application/json' },
790
+ body: JSON.stringify({ view })
791
+ });
792
+ }, 1000);
793
+ }
794
+ });
795
+ ```
796
+
797
+ ### What to Persist
798
+
799
+ Common preferences to save:
800
+ - **View mode**: `'month'`, `'week'`, `'day'`, or `'list'`
801
+ - **Current date**: Last viewed date for returning to the same position
802
+ - **Enabled views**: Which view modes the user prefers to have available
803
+ - **UI preferences**: Dark mode, show/hide options
804
+
805
+ **Note**: The calendar is designed to be stateless - all state management is your responsibility, giving you full flexibility over storage method, persistence duration, and privacy controls.
806
+
483
807
  ## Internationalization
484
808
 
485
809
  SimpleCalendarJs uses the native Intl.DateTimeFormat API for full locale support. Simply pass a valid locale code:
@@ -686,6 +1010,9 @@ Example with a complete brand color scheme:
686
1010
  - `--cal-tooltip-max-width`, `--cal-tooltip-padding`, `--cal-tooltip-radius` - Tooltip sizing
687
1011
  - `--cal-tooltip-font-size`, `--cal-tooltip-offset` - Tooltip typography
688
1012
 
1013
+ **Loading Overlay:**
1014
+ - `--cal-loading-bg` - Loading spinner overlay background (default: `rgba(255, 255, 255, 0.7)` in light mode, `rgba(31, 41, 55, 0.7)` in dark mode)
1015
+
689
1016
  All styles are scoped under `.uc-calendar` to prevent conflicts with your existing CSS.
690
1017
 
691
1018
  ## Framework Wrapper APIs
@@ -803,6 +1130,7 @@ For commercial licensing inquiries: simplecalendarjs@gmail.com
803
1130
 
804
1131
  - **Documentation**: [docs.html](docs.html)
805
1132
  - **Demo**: [index.html](index.html)
1133
+ - **Changelog**: [CHANGELOG.md](CHANGELOG.md)
806
1134
  - **Repository**: [github.com/pclslopes/SimpleCalendarJs](https://github.com/pclslopes/SimpleCalendarJs)
807
1135
  - **NPM**: [npmjs.com/package/simple-calendar-js](https://www.npmjs.com/package/simple-calendar-js)
808
1136
  - **Website**: [simplecalendarjs.com](https://www.simplecalendarjs.com)
@@ -1,5 +1,5 @@
1
1
  /**
2
- * SimpleCalendarJs v3.0.4 — simple-calendar-js.css
2
+ * SimpleCalendarJs v3.0.6 — simple-calendar-js.css
3
3
  * A clean, modern, and feature-rich JavaScript calendar component with zero dependencies
4
4
  *
5
5
  * @author Pedro Lopes <simplecalendarjs@gmail.com>
@@ -9,4 +9,4 @@
9
9
  * All styles scoped under .uc-calendar to prevent leaking.
10
10
  * Override any --cal-* variable in :root or on .uc-calendar.
11
11
  */
12
- :root{--cal-bg:#ffffff;--cal-text:#111827;--cal-text-subtle:#6b7280;--cal-text-muted:#9ca3af;--cal-border:#e5e7eb;--cal-border-strong:#d1d5db;--cal-primary:#4f46e5;--cal-primary-dark:#4338ca;--cal-primary-light:#eef2ff;--cal-event-bg:var(--cal-primary);--cal-event-text:#ffffff;--cal-event-border-radius:3px;--cal-today-bg:#eef2ff;--cal-today-text:var(--cal-primary);--cal-hover:#f9fafb;--cal-hover-strong:#f3f4f6;--cal-selected-bg:#ede9fe;--cal-font-family:inherit;--cal-font-size:13px;--cal-time-col-width:64px;--cal-hour-height:60px;--cal-cell-min-height:112px;--cal-event-height:22px;--cal-event-gap:2px;--cal-header-day-height:30px;--cal-day-name-height:36px;--cal-now-color:#ef4444;--cal-toolbar-bg:var(--cal-bg);--cal-radius:8px;--cal-shadow:0 1px 3px rgba(0,0,0,.08),0 1px 2px rgba(0,0,0,.06);--cal-transition:150ms ease;--cal-tooltip-bg:#1f2937;--cal-tooltip-text:#f9fafb;--cal-tooltip-border:#374151;--cal-tooltip-shadow:0 4px 12px rgba(0, 0, 0, 0.15);--cal-tooltip-max-width:250px;--cal-tooltip-padding:8px 12px;--cal-tooltip-radius:6px;--cal-tooltip-font-size:12px;--cal-tooltip-offset:8px}.uc-calendar{font-family:var(--cal-font-family);font-size:var(--cal-font-size);color:var(--cal-text);background:var(--cal-bg);border:1px solid var(--cal-border);border-radius:var(--cal-radius);overflow:visible;display:flex;flex-direction:column;position:relative;min-height:500px;-webkit-font-smoothing:antialiased}.uc-calendar *,.uc-calendar ::after,.uc-calendar ::before{box-sizing:border-box;margin:0;padding:0}.uc-toolbar{display:flex;align-items:center;justify-content:space-between;gap:8px;padding:10px 14px;background:var(--cal-toolbar-bg);border-bottom:1px solid var(--cal-border);border-radius:var(--cal-radius) var(--cal-radius) 0 0;flex-shrink:0;flex-wrap:wrap}.uc-toolbar-section{display:flex;align-items:center;gap:4px}.uc-toolbar-center{flex:1;display:flex;justify-content:center;position:relative}.uc-title{font-size:15px;font-weight:600;color:var(--cal-text);white-space:nowrap;letter-spacing:-.01em;display:flex;align-items:baseline;gap:5px}.uc-title-main{text-transform:capitalize}.uc-year-btn{background:0 0;border:none;font:inherit;font-weight:600;font-size:15px;color:var(--cal-primary);cursor:pointer;padding:1px 4px;border-radius:4px;line-height:inherit;text-decoration:underline;text-decoration-style:dotted;text-underline-offset:2px;transition:background var(--cal-transition),color var(--cal-transition)}.uc-year-btn.uc-open,.uc-year-btn:hover{background:var(--cal-primary-light);text-decoration:none}.uc-year-picker{position:absolute;top:calc(100% + 8px);left:50%;transform:translateX(-50%);background:var(--cal-bg);border:1px solid var(--cal-border);border-radius:10px;box-shadow:0 4px 24px rgba(0,0,0,.12);padding:10px;z-index:200;min-width:210px}.uc-year-picker-nav{display:flex;align-items:center;justify-content:space-between;margin-bottom:8px}.uc-year-range{font-size:12px;font-weight:600;color:var(--cal-text-subtle)}.uc-year-nav-btn{background:0 0;border:none;font-size:18px;line-height:1;color:var(--cal-text);cursor:pointer;padding:2px 7px;border-radius:5px;font-family:inherit;transition:background var(--cal-transition)}.uc-year-nav-btn:hover{background:var(--cal-hover-strong)}.uc-year-grid{display:grid;grid-template-columns:repeat(4,1fr);gap:4px}.uc-year-item{background:0 0;border:none;border-radius:6px;font-size:13px;font-weight:500;font-family:inherit;color:var(--cal-text);cursor:pointer;padding:7px 4px;text-align:center;transition:background var(--cal-transition),color var(--cal-transition)}.uc-year-item:hover{background:var(--cal-hover-strong)}.uc-year-item.uc-today-year{color:var(--cal-primary);font-weight:700}.uc-year-item.uc-active{background:var(--cal-primary);color:#fff;font-weight:700}.uc-btn{display:inline-flex;align-items:center;justify-content:center;gap:4px;border:1px solid var(--cal-border);background:var(--cal-bg);color:var(--cal-text);border-radius:6px;padding:5px 10px;font-size:13px;font-family:inherit;font-weight:500;cursor:pointer;white-space:nowrap;line-height:1.4;transition:background var(--cal-transition),border-color var(--cal-transition),color var(--cal-transition);user-select:none}.uc-btn:hover{background:var(--cal-hover-strong);border-color:var(--cal-border-strong)}.uc-btn:active{background:var(--cal-selected-bg)}.uc-btn:focus-visible{outline:2px solid var(--cal-primary);outline-offset:2px}.uc-nav-btn{font-size:18px;padding:4px 8px;line-height:1;border-color:transparent;background:0 0}.uc-nav-btn:hover{border-color:var(--cal-border)}.uc-today-btn{padding:5px 12px}.uc-today-btn.uc-active{background:var(--cal-primary-light);color:var(--cal-primary);font-weight:600;border-color:transparent}.uc-view-switcher{display:flex;border:1px solid var(--cal-border);border-radius:6px;overflow:hidden}.uc-view-btn{border:none;border-radius:0;border-right:1px solid var(--cal-border);background:0 0;color:var(--cal-text-subtle);font-weight:500;padding:5px 11px}.uc-view-btn:last-child{border-right:none}.uc-view-btn:hover{background:var(--cal-hover-strong);color:var(--cal-text);border-color:transparent}.uc-view-btn.uc-active{background:var(--cal-primary-light);color:var(--cal-primary);font-weight:600}.uc-loading{position:absolute;inset:0;display:flex;align-items:center;justify-content:center;background:rgba(255,255,255,.7);z-index:100;pointer-events:none}.uc-spinner{width:28px;height:28px;border:3px solid var(--cal-border);border-top-color:var(--cal-primary);border-radius:50%;animation:uc-spin .6s linear infinite}@keyframes uc-spin{to{transform:rotate(360deg)}}.uc-view-container{flex:1;overflow:visible;display:flex;flex-direction:column}.uc-view-container:first-child .uc-month-header,.uc-view-container:first-child .uc-week-header{border-radius:var(--cal-radius) var(--cal-radius) 0 0}.uc-month-view{display:flex;flex-direction:column;flex:1;overflow:visible;border-radius:0 0 var(--cal-radius) var(--cal-radius)}.uc-month-header{display:grid;grid-template-columns:repeat(7,1fr);border-bottom:1px solid var(--cal-border);flex-shrink:0}.uc-month-day-name{height:var(--cal-day-name-height,36px);display:flex;align-items:center;justify-content:center;font-size:11px;font-weight:600;text-transform:uppercase;letter-spacing:.05em;color:var(--cal-text-subtle);user-select:none}.uc-month-body{flex:1;display:flex;flex-direction:column;overflow:visible}.uc-week-row{flex:1;position:relative;min-height:var(--cal-cell-min-height);border-bottom:1px solid var(--cal-border)}.uc-week-row:last-child{border-bottom:none;border-radius:0 0 var(--cal-radius) var(--cal-radius)}.uc-week-cells{position:absolute;inset:0;display:grid;grid-template-columns:repeat(7,1fr);z-index:1}.uc-day-cell{border-right:1px solid var(--cal-border);padding:4px 6px 4px 6px;cursor:pointer;transition:background var(--cal-transition);min-height:var(--cal-cell-min-height)}.uc-day-cell:last-child{border-right:none}.uc-day-cell:hover{background:var(--cal-hover)}.uc-day-cell.uc-today{background:var(--cal-today-bg)}.uc-day-cell.uc-other-month .uc-day-number{color:var(--cal-text-muted)}.uc-day-number{display:inline-flex;align-items:center;justify-content:center;width:26px;height:26px;font-size:13px;font-weight:500;border-radius:50%;color:var(--cal-text);cursor:pointer;transition:background var(--cal-transition),color var(--cal-transition);line-height:1}.uc-day-number:hover{background:var(--cal-hover-strong)}.uc-today .uc-day-number{background:var(--cal-primary);color:#fff;font-weight:700}.uc-today .uc-day-number:hover{background:var(--cal-primary-dark)}.uc-week-events{position:absolute;inset:0;z-index:2;pointer-events:none;overflow:visible}.uc-event-bar{position:absolute;height:var(--cal-event-height);background:var(--cal-event-bg);color:var(--cal-event-text);border-radius:var(--cal-event-border-radius);display:flex;align-items:center;gap:4px;padding:0 6px;cursor:pointer;pointer-events:auto;white-space:nowrap;font-size:12px;font-weight:500;transition:filter var(--cal-transition),opacity var(--cal-transition);z-index:3}.uc-event-bar:hover{filter:brightness(.92);z-index:1000}.uc-event-bar:active{filter:brightness(.85)}.uc-event-bar.uc-continues-left{border-top-left-radius:0;border-bottom-left-radius:0}.uc-event-bar.uc-continues-right{border-top-right-radius:0;border-bottom-right-radius:0}.uc-event-title{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;flex:1;min-width:0}.uc-event-time{font-size:11px;opacity:.85;flex-shrink:0}.uc-more-link{position:absolute;height:16px;display:flex;align-items:center;padding:0 6px;font-size:10px;font-weight:600;color:var(--cal-text-subtle);cursor:pointer;pointer-events:auto;border-radius:var(--cal-event-border-radius);white-space:nowrap;transition:background var(--cal-transition),color var(--cal-transition);z-index:3}.uc-more-link:hover{background:var(--cal-hover-strong);color:var(--cal-text)}.uc-day-view,.uc-week-view{display:flex;flex-direction:column;flex:1;overflow:visible;border-radius:0 0 var(--cal-radius) var(--cal-radius)}.uc-week-header{display:flex;border-bottom:1px solid var(--cal-border);flex-shrink:0;background:var(--cal-bg)}.uc-time-gutter-spacer{width:var(--cal-time-col-width);flex-shrink:0;border-right:1px solid var(--cal-border)}.uc-all-day-label{display:flex;align-items:center;justify-content:center;font-size:10px;font-weight:600;text-transform:uppercase;letter-spacing:.06em;color:var(--cal-text-muted);white-space:nowrap}.uc-week-day-headers{flex:1;display:grid;grid-template-columns:repeat(7,1fr)}.uc-week-day-headers.uc-day-header-single{grid-template-columns:1fr}.uc-week-day-header{padding:8px 4px;display:flex;flex-direction:column;align-items:center;gap:2px;border-right:1px solid var(--cal-border);cursor:default}.uc-week-day-header:last-child{border-right:none}.uc-week-day-name{font-size:11px;font-weight:600;text-transform:uppercase;letter-spacing:.05em;color:var(--cal-text-subtle);user-select:none}.uc-week-day-num{display:inline-flex;align-items:center;justify-content:center;width:30px;height:30px;font-size:16px;font-weight:600;border-radius:50%;color:var(--cal-text);cursor:pointer;transition:background var(--cal-transition),color var(--cal-transition)}.uc-week-day-num:hover{background:var(--cal-hover-strong)}.uc-week-day-header.uc-today .uc-week-day-name{color:var(--cal-primary)}.uc-week-day-header.uc-today .uc-week-day-num{background:var(--cal-primary);color:#fff}.uc-week-day-header.uc-today .uc-week-day-num:hover{background:var(--cal-primary-dark)}.uc-all-day-section{display:flex;border-bottom:1px solid var(--cal-border);flex-shrink:0;background:var(--cal-bg)}.uc-all-day-events{flex:1;position:relative;min-height:calc(var(--cal-event-height) + 6px);padding:2px 0}.uc-time-body{flex:1;overflow-y:auto;overflow-x:visible;position:relative;border-radius:0 0 var(--cal-radius) var(--cal-radius)}.uc-time-body::-webkit-scrollbar{width:6px}.uc-time-body::-webkit-scrollbar-track{background:0 0}.uc-time-body::-webkit-scrollbar-thumb{background:var(--cal-border-strong);border-radius:3px}.uc-time-grid-inner{display:flex;flex-direction:row;height:calc(24 * var(--cal-hour-height));position:relative}.uc-time-gutter{width:var(--cal-time-col-width);flex-shrink:0;border-right:1px solid var(--cal-border);position:relative}.uc-hour-cell{height:var(--cal-hour-height);position:relative;display:flex;align-items:flex-start;justify-content:flex-end;padding-right:8px}.uc-hour-label{font-size:11px;font-weight:500;color:var(--cal-text-subtle);user-select:none;pointer-events:none;white-space:nowrap;transform:translateY(-50%);margin-top:1px}.uc-time-columns{flex:1;display:grid;grid-template-columns:repeat(var(--uc-col-count,7),1fr);position:relative}.uc-time-col{position:relative;border-right:1px solid var(--cal-border);height:calc(24 * var(--cal-hour-height));cursor:pointer}.uc-time-col:last-child{border-right:none}.uc-time-col.uc-today{background:var(--cal-today-bg)}.uc-hour-row{height:var(--cal-hour-height);border-bottom:1px solid var(--cal-border);position:relative;pointer-events:none}.uc-half-hour-line{position:absolute;top:50%;left:0;right:0;border-top:1px dashed var(--cal-border);pointer-events:none}.uc-timed-event{position:absolute;background:var(--cal-event-bg);color:var(--cal-event-text);border-radius:var(--cal-event-border-radius);padding:3px 6px;cursor:pointer;display:flex;flex-direction:column;gap:1px;font-size:12px;font-weight:500;border-left:3px solid rgba(0,0,0,.15);transition:filter var(--cal-transition),box-shadow var(--cal-transition);z-index:2;min-height:18px}.uc-timed-event:hover{filter:brightness(.92);box-shadow:0 2px 8px rgba(0,0,0,.15);z-index:1000}.uc-timed-event .uc-event-title{font-weight:600;line-height:1.3;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.uc-timed-event .uc-event-time{font-size:11px;opacity:.85;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.uc-timed-event--short{flex-direction:row;align-items:center;gap:4px}.uc-timed-event--short .uc-event-time{flex-shrink:0}.uc-timed-event--short .uc-event-title{flex:1;min-width:0}.uc-now-indicator{position:absolute;left:0;right:0;pointer-events:none;z-index:10;display:flex;align-items:center}.uc-now-dot{width:10px;height:10px;border-radius:50%;background:var(--cal-now-color);flex-shrink:0;margin-left:-5px}.uc-now-line{flex:1;height:2px;background:var(--cal-now-color)}.uc-list-view{padding:0;overflow:visible;border-radius:0 0 var(--cal-radius) var(--cal-radius)}.uc-list-empty{display:flex;align-items:center;justify-content:center;min-height:300px;color:var(--cal-text-muted);font-size:14px}.uc-list-date-group{margin-bottom:24px;overflow:visible}.uc-list-date-group:last-child{margin-bottom:0}.uc-list-events{background:var(--cal-bg);overflow:visible}.uc-list-event{display:flex;align-items:flex-start;gap:12px;padding:12px 16px;border-bottom:1px solid var(--cal-border);cursor:pointer;transition:background var(--cal-transition);position:relative;overflow:visible}.uc-list-date-header{font-size:14px;font-weight:600;color:var(--cal-text);padding:12px 16px;background:var(--cal-bg-secondary);border-bottom:1px solid var(--cal-border);position:sticky;top:0;z-index:1;overflow:visible}.uc-list-event:hover{background:var(--cal-hover)}.uc-list-event:last-child{border-bottom:none}.uc-list-event-indicator{width:8px;height:8px;border-radius:50%;margin-top:4px;flex-shrink:0}.uc-list-event-time{font-size:13px;font-weight:500;color:var(--cal-text-secondary);min-width:80px;flex-shrink:0}.uc-list-event-content{flex:1;min-width:0}.uc-list-event-title{font-size:14px;font-weight:500;color:var(--cal-text);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.uc-calendar [data-tooltip]:not([data-tooltip=""]):hover::after,.uc-calendar [data-tooltip]:not([data-tooltip=""]):hover::before{opacity:1;visibility:visible;transition-delay:0.4s}.uc-calendar [data-tooltip]:not([data-tooltip=""])::before{content:attr(data-tooltip);position:absolute;bottom:calc(100% + var(--cal-tooltip-offset));left:50%;transform:translateX(-50%);background:var(--cal-tooltip-bg);color:var(--cal-tooltip-text);border:1px solid var(--cal-tooltip-border);box-shadow:var(--cal-tooltip-shadow);padding:var(--cal-tooltip-padding);border-radius:var(--cal-tooltip-radius);font-size:var(--cal-tooltip-font-size);font-weight:500;line-height:1.4;max-width:var(--cal-tooltip-max-width);width:max-content;white-space:pre-wrap;word-wrap:break-word;text-align:left;z-index:1000;pointer-events:none;opacity:0;visibility:hidden;transition:opacity .2s ease,visibility .2s ease}.uc-calendar [data-tooltip]:not([data-tooltip=""])::after{content:'';position:absolute;bottom:calc(100% + var(--cal-tooltip-offset) - 5px);left:50%;transform:translateX(-50%);width:0;height:0;border-left:5px solid transparent;border-right:5px solid transparent;border-top:5px solid var(--cal-tooltip-bg);z-index:1002;pointer-events:none;opacity:0;visibility:hidden;transition:opacity .2s ease,visibility .2s ease}.uc-calendar [data-tooltip]:not([data-tooltip=""])::before{filter:drop-shadow(0 1px 0 var(--cal-tooltip-border))}.uc-calendar [data-tooltip].uc-tooltip-left::before{left:auto;right:0;transform:translateX(0)}.uc-calendar [data-tooltip].uc-tooltip-left::after{left:auto;right:12px;transform:translateX(0)}.uc-calendar [data-tooltip].uc-tooltip-right::before{left:0;transform:translateX(0)}.uc-calendar [data-tooltip].uc-tooltip-right::after{left:12px;transform:translateX(0)}.uc-calendar [data-tooltip].uc-tooltip-bottom::before{bottom:auto;top:calc(100% + var(--cal-tooltip-offset));filter:drop-shadow(0 -1px 0 var(--cal-tooltip-border))}.uc-calendar [data-tooltip].uc-tooltip-bottom::after{bottom:auto;top:calc(100% + var(--cal-tooltip-offset) - 5px);border-top:5px solid var(--cal-tooltip-bg);border-left:5px solid transparent;border-right:5px solid transparent;border-bottom:none;transform:translateX(-50%) rotate(180deg)}@media (max-width:768px){.uc-toolbar{padding:8px 10px}.uc-title{font-size:14px}.uc-toolbar-center{order:-1;width:100%;flex:none}.uc-toolbar-section{justify-content:center}.uc-view-btn{padding:5px 8px;font-size:12px}:root{--cal-time-col-width:52px;--cal-hour-height:52px;--cal-cell-min-height:88px;--cal-event-height:20px}.uc-week-day-num{width:24px;height:24px;font-size:13px}.uc-week-day-name{font-size:10px}.uc-hour-cell:nth-child(odd) .uc-hour-label{visibility:hidden}}@media (max-width:480px){.uc-today-btn{display:none}.uc-toolbar-section.uc-toolbar-right{gap:2px}}.uc-calendar.uc-no-grid .uc-day-cell,.uc-calendar.uc-no-grid .uc-day-header-row,.uc-calendar.uc-no-grid .uc-day-name-row,.uc-calendar.uc-no-grid .uc-grid-line,.uc-calendar.uc-no-grid .uc-hour-cell,.uc-calendar.uc-no-grid .uc-time-col,.uc-calendar.uc-no-grid .uc-time-gutter,.uc-calendar.uc-no-grid .uc-week-day-header,.uc-calendar.uc-no-grid .uc-week-row{border:none!important}.uc-calendar.uc-no-grid .uc-all-day-section{border-top:none!important;border-left:none!important;border-right:none!important}.uc-calendar.uc-no-border{border:none!important}.uc-calendar.uc-dark,.uc-dark .uc-calendar{--cal-bg:#1f2937;--cal-text:#f9fafb;--cal-text-subtle:#9ca3af;--cal-text-muted:#6b7280;--cal-border:#374151;--cal-border-strong:#4b5563;--cal-hover:#374151;--cal-hover-strong:#4b5563;--cal-selected-bg:#78716c;--cal-today-bg:#57534e;--cal-primary-light:#57534e;--cal-toolbar-bg:#111827;--cal-tooltip-bg:#374151;--cal-tooltip-text:#f9fafb;--cal-tooltip-border:#4b5563}@media print{.uc-toolbar{display:none}.uc-time-body{overflow:visible}.uc-calendar{border:none}}
12
+ :root{--cal-bg:#ffffff;--cal-bg-secondary:#f9fafb;--cal-text:#111827;--cal-text-subtle:#6b7280;--cal-text-muted:#9ca3af;--cal-border:#e5e7eb;--cal-border-strong:#d1d5db;--cal-primary:#4f46e5;--cal-primary-dark:#4338ca;--cal-primary-light:#eef2ff;--cal-event-bg:var(--cal-primary);--cal-event-text:#ffffff;--cal-event-border-radius:3px;--cal-today-bg:#eef2ff;--cal-today-text:var(--cal-primary);--cal-hover:#f9fafb;--cal-hover-strong:#f3f4f6;--cal-selected-bg:#ede9fe;--cal-font-family:inherit;--cal-font-size:13px;--cal-time-col-width:64px;--cal-hour-height:60px;--cal-cell-min-height:112px;--cal-event-height:22px;--cal-event-gap:2px;--cal-header-day-height:30px;--cal-day-name-height:36px;--cal-now-color:#ef4444;--cal-toolbar-bg:var(--cal-bg);--cal-radius:8px;--cal-shadow:0 1px 3px rgba(0,0,0,.08),0 1px 2px rgba(0,0,0,.06);--cal-transition:150ms ease;--cal-tooltip-bg:#1f2937;--cal-tooltip-text:#f9fafb;--cal-tooltip-border:#374151;--cal-tooltip-shadow:0 4px 12px rgba(0, 0, 0, 0.15);--cal-tooltip-max-width:250px;--cal-tooltip-padding:8px 12px;--cal-tooltip-radius:6px;--cal-tooltip-font-size:12px;--cal-tooltip-offset:8px;--cal-loading-bg:rgba(255, 255, 255, 0.7)}.uc-calendar{font-family:var(--cal-font-family);font-size:var(--cal-font-size);color:var(--cal-text);background:var(--cal-bg);border:1px solid var(--cal-border);border-radius:var(--cal-radius);overflow:hidden;display:flex;flex-direction:column;position:relative;-webkit-font-smoothing:antialiased}.uc-calendar *,.uc-calendar ::after,.uc-calendar ::before{box-sizing:border-box;margin:0;padding:0}.uc-toolbar{display:flex;align-items:center;justify-content:space-between;gap:8px;padding:10px 14px;background:var(--cal-toolbar-bg);border-bottom:1px solid var(--cal-border);border-radius:var(--cal-radius) var(--cal-radius) 0 0;flex-shrink:0;flex-wrap:wrap}.uc-toolbar-section{display:flex;align-items:center;gap:4px}.uc-toolbar-center{flex:1;display:flex;justify-content:center;position:relative}.uc-title{font-size:15px;font-weight:600;color:var(--cal-text);white-space:nowrap;letter-spacing:-.01em;display:flex;align-items:baseline;gap:5px}.uc-title-main{text-transform:capitalize}.uc-year-btn{background:0 0;border:none;font:inherit;font-weight:600;font-size:15px;color:var(--cal-primary);cursor:pointer;padding:1px 4px;border-radius:4px;line-height:inherit;text-decoration:underline;text-decoration-style:dotted;text-underline-offset:2px;transition:background var(--cal-transition),color var(--cal-transition)}.uc-year-btn.uc-open,.uc-year-btn:hover{background:var(--cal-primary-light);text-decoration:none}.uc-year-picker{position:absolute;top:calc(100% + 8px);left:50%;transform:translateX(-50%);background:var(--cal-bg);border:1px solid var(--cal-border);border-radius:10px;box-shadow:0 4px 24px rgba(0,0,0,.12);padding:10px;z-index:200;min-width:210px}.uc-year-picker-nav{display:flex;align-items:center;justify-content:space-between;margin-bottom:8px}.uc-year-range{font-size:12px;font-weight:600;color:var(--cal-text-subtle)}.uc-year-nav-btn{background:0 0;border:none;font-size:18px;line-height:1;color:var(--cal-text);cursor:pointer;padding:2px 7px;border-radius:5px;font-family:inherit;transition:background var(--cal-transition)}.uc-year-nav-btn:hover{background:var(--cal-hover-strong)}.uc-year-grid{display:grid;grid-template-columns:repeat(4,1fr);gap:4px}.uc-year-item{background:0 0;border:none;border-radius:6px;font-size:13px;font-weight:500;font-family:inherit;color:var(--cal-text);cursor:pointer;padding:7px 4px;text-align:center;transition:background var(--cal-transition),color var(--cal-transition)}.uc-year-item:hover{background:var(--cal-hover-strong)}.uc-year-item.uc-today-year{color:var(--cal-primary);font-weight:700}.uc-year-item.uc-active{background:var(--cal-primary);color:#fff;font-weight:700}.uc-btn{display:inline-flex;align-items:center;justify-content:center;gap:4px;border:1px solid var(--cal-border);background:var(--cal-bg);color:var(--cal-text);border-radius:6px;padding:5px 10px;font-size:13px;font-family:inherit;font-weight:500;cursor:pointer;white-space:nowrap;line-height:1.4;transition:background var(--cal-transition),border-color var(--cal-transition),color var(--cal-transition);user-select:none}.uc-btn:hover{background:var(--cal-hover-strong);border-color:var(--cal-border-strong)}.uc-btn:active{background:var(--cal-selected-bg)}.uc-btn:focus-visible{outline:2px solid var(--cal-primary);outline-offset:2px}.uc-nav-btn{font-size:18px;padding:4px 8px;line-height:1;border-color:transparent;background:0 0}.uc-nav-btn:hover{border-color:var(--cal-border)}.uc-today-btn{padding:5px 12px}.uc-today-btn.uc-active{background:var(--cal-primary-light);color:var(--cal-primary);font-weight:600;border-color:transparent}.uc-view-switcher{display:flex;border:1px solid var(--cal-border);border-radius:6px;overflow:hidden}.uc-view-btn{border:none;border-radius:0;border-right:1px solid var(--cal-border);background:0 0;color:var(--cal-text-subtle);font-weight:500;padding:5px 11px}.uc-view-btn:last-child{border-right:none}.uc-view-btn:hover{background:var(--cal-hover-strong);color:var(--cal-text);border-color:transparent}.uc-view-btn.uc-active{background:var(--cal-primary-light);color:var(--cal-primary);font-weight:600}.uc-loading{position:absolute;inset:0;display:flex;align-items:center;justify-content:center;background:var(--cal-loading-bg);z-index:100;pointer-events:none}.uc-spinner{width:28px;height:28px;border:3px solid var(--cal-border);border-top-color:var(--cal-primary);border-radius:50%;animation:uc-spin .6s linear infinite}@keyframes uc-spin{to{transform:rotate(360deg)}}.uc-view-container{flex:1;overflow:visible;display:flex;flex-direction:column}.uc-view-container:first-child .uc-month-header,.uc-view-container:first-child .uc-week-header{border-radius:var(--cal-radius) var(--cal-radius) 0 0}.uc-month-view{display:flex;flex-direction:column;flex:1;overflow:visible;border-radius:0 0 var(--cal-radius) var(--cal-radius)}.uc-month-header{display:grid;grid-template-columns:repeat(7,1fr);border-bottom:1px solid var(--cal-border);flex-shrink:0}.uc-month-day-name{height:var(--cal-day-name-height,36px);display:flex;align-items:center;justify-content:center;font-size:11px;font-weight:600;text-transform:uppercase;letter-spacing:.05em;color:var(--cal-text-subtle);user-select:none}.uc-month-body{flex:1;display:flex;flex-direction:column;overflow:visible}.uc-week-row{flex:1;position:relative;min-height:var(--cal-cell-min-height);border-bottom:1px solid var(--cal-border)}.uc-week-row:last-child{border-bottom:none;border-radius:0 0 var(--cal-radius) var(--cal-radius)}.uc-week-cells{position:absolute;inset:0;display:grid;grid-template-columns:repeat(7,1fr);z-index:1}.uc-day-cell{border-right:1px solid var(--cal-border);padding:4px 6px 4px 6px;cursor:pointer;transition:background var(--cal-transition);min-height:var(--cal-cell-min-height)}.uc-day-cell:last-child{border-right:none}.uc-day-cell:hover{background:var(--cal-hover)}.uc-day-cell.uc-today{background:var(--cal-today-bg)}.uc-day-cell.uc-other-month .uc-day-number{color:var(--cal-text-muted)}.uc-day-number{display:inline-flex;align-items:center;justify-content:center;width:26px;height:26px;font-size:13px;font-weight:500;border-radius:50%;color:var(--cal-text);cursor:pointer;transition:background var(--cal-transition),color var(--cal-transition);line-height:1}.uc-day-number:hover{background:var(--cal-hover-strong)}.uc-today .uc-day-number{background:var(--cal-primary);color:#fff;font-weight:700}.uc-today .uc-day-number:hover{background:var(--cal-primary-dark)}.uc-week-events{position:absolute;inset:0;z-index:2;pointer-events:none;overflow:visible}.uc-event-bar{position:absolute;height:var(--cal-event-height);background:var(--cal-event-bg);color:var(--cal-event-text);border-radius:var(--cal-event-border-radius);display:flex;align-items:center;gap:4px;padding:0 6px;cursor:pointer;pointer-events:auto;white-space:nowrap;font-size:12px;font-weight:500;transition:filter var(--cal-transition),opacity var(--cal-transition);z-index:3}.uc-event-bar:hover{filter:brightness(.92);z-index:1000}.uc-event-bar:active{filter:brightness(.85)}.uc-event-bar.uc-continues-left{border-top-left-radius:0;border-bottom-left-radius:0}.uc-event-bar.uc-continues-right{border-top-right-radius:0;border-bottom-right-radius:0}.uc-event-title{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;flex:1;min-width:0}.uc-event-time{font-size:11px;opacity:.85;flex-shrink:0}.uc-more-link{position:absolute;height:16px;display:flex;align-items:center;padding:0 6px;font-size:10px;font-weight:600;color:var(--cal-text-subtle);cursor:pointer;pointer-events:auto;border-radius:var(--cal-event-border-radius);white-space:nowrap;transition:background var(--cal-transition),color var(--cal-transition);z-index:3}.uc-more-link:hover{background:var(--cal-hover-strong);color:var(--cal-text)}.uc-day-view,.uc-week-view{display:flex;flex-direction:column;max-height:650px;overflow:hidden;border-radius:0 0 var(--cal-radius) var(--cal-radius)}.uc-week-header{display:flex;border-bottom:1px solid var(--cal-border);flex-shrink:0;background:var(--cal-bg)}.uc-time-gutter-spacer{width:var(--cal-time-col-width);flex-shrink:0;border-right:1px solid var(--cal-border)}.uc-all-day-label{display:flex;align-items:center;justify-content:center;font-size:10px;font-weight:600;text-transform:uppercase;letter-spacing:.06em;color:var(--cal-text-muted);white-space:nowrap}.uc-week-day-headers{flex:1;display:grid;grid-template-columns:repeat(7,1fr)}.uc-week-day-headers.uc-day-header-single{grid-template-columns:1fr}.uc-week-day-header{padding:8px 4px;display:flex;flex-direction:column;align-items:center;gap:2px;border-right:1px solid var(--cal-border);cursor:default}.uc-week-day-header:last-child{border-right:none}.uc-week-day-name{font-size:11px;font-weight:600;text-transform:uppercase;letter-spacing:.05em;color:var(--cal-text-subtle);user-select:none}.uc-week-day-num{display:inline-flex;align-items:center;justify-content:center;width:30px;height:30px;font-size:16px;font-weight:600;border-radius:50%;color:var(--cal-text);cursor:pointer;transition:background var(--cal-transition),color var(--cal-transition)}.uc-week-day-num:hover{background:var(--cal-hover-strong)}.uc-week-day-header.uc-today .uc-week-day-name{color:var(--cal-primary)}.uc-week-day-header.uc-today .uc-week-day-num{background:var(--cal-primary);color:#fff}.uc-week-day-header.uc-today .uc-week-day-num:hover{background:var(--cal-primary-dark)}.uc-all-day-section{display:flex;border-bottom:1px solid var(--cal-border);flex-shrink:0;background:var(--cal-bg)}.uc-all-day-events{flex:1;position:relative;min-height:calc(var(--cal-event-height) + 6px);padding:2px 0}.uc-time-body{flex:1;overflow-y:auto;overflow-x:visible;position:relative;border-radius:0 0 var(--cal-radius) var(--cal-radius)}.uc-time-body::-webkit-scrollbar{width:6px}.uc-time-body::-webkit-scrollbar-track{background:0 0}.uc-time-body::-webkit-scrollbar-thumb{background:var(--cal-border-strong);border-radius:3px}.uc-time-grid-inner{display:flex;flex-direction:row;height:calc(24 * var(--cal-hour-height));position:relative}.uc-time-gutter{width:var(--cal-time-col-width);flex-shrink:0;border-right:1px solid var(--cal-border);position:relative}.uc-hour-cell{height:var(--cal-hour-height);position:relative;display:flex;align-items:flex-start;justify-content:flex-end;padding-right:8px}.uc-hour-label{font-size:11px;font-weight:500;color:var(--cal-text-subtle);user-select:none;pointer-events:none;white-space:nowrap;transform:translateY(-50%);margin-top:1px}.uc-time-columns{flex:1;display:grid;grid-template-columns:repeat(var(--uc-col-count,7),1fr);position:relative}.uc-time-col{position:relative;border-right:1px solid var(--cal-border);height:calc(24 * var(--cal-hour-height));cursor:pointer}.uc-time-col:last-child{border-right:none}.uc-time-col.uc-today{background:var(--cal-today-bg)}.uc-hour-row{height:var(--cal-hour-height);border-bottom:1px solid var(--cal-border);position:relative;pointer-events:none}.uc-half-hour-line{position:absolute;top:50%;left:0;right:0;border-top:1px dashed var(--cal-border);pointer-events:none}.uc-timed-event{position:absolute;background:var(--cal-event-bg);color:var(--cal-event-text);border-radius:var(--cal-event-border-radius);padding:3px 6px;cursor:pointer;display:flex;flex-direction:column;gap:1px;font-size:12px;font-weight:500;border-left:3px solid rgba(0,0,0,.15);transition:filter var(--cal-transition),box-shadow var(--cal-transition);z-index:2;min-height:18px}.uc-timed-event:hover{filter:brightness(.92);box-shadow:0 2px 8px rgba(0,0,0,.15);z-index:1000}.uc-timed-event .uc-event-title{font-weight:600;line-height:1.3;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.uc-timed-event .uc-event-time{font-size:11px;opacity:.85;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.uc-timed-event--short{flex-direction:row;align-items:center;gap:4px}.uc-timed-event--short .uc-event-time{flex-shrink:0}.uc-timed-event--short .uc-event-title{flex:1;min-width:0}.uc-now-indicator{position:absolute;left:0;right:0;pointer-events:none;z-index:10;display:flex;align-items:center}.uc-now-dot{width:10px;height:10px;border-radius:50%;background:var(--cal-now-color);flex-shrink:0;margin-left:-5px}.uc-now-line{flex:1;height:2px;background:var(--cal-now-color)}.uc-list-view{padding:0;max-height:650px;overflow-y:auto;overflow-x:hidden;border-radius:0 0 var(--cal-radius) var(--cal-radius)}.uc-list-view::-webkit-scrollbar{width:6px}.uc-list-view::-webkit-scrollbar-track{background:0 0}.uc-list-view::-webkit-scrollbar-thumb{background:var(--cal-border-strong);border-radius:3px}.uc-list-empty{display:flex;align-items:center;justify-content:center;min-height:300px;color:var(--cal-text-muted);font-size:14px}.uc-list-date-group{margin-bottom:24px;overflow:visible}.uc-list-date-group:last-child{margin-bottom:0}.uc-list-events{background:var(--cal-bg);overflow:visible}.uc-list-event{display:flex;align-items:flex-start;gap:12px;padding:12px 16px;border-bottom:1px solid var(--cal-border);cursor:pointer;transition:background var(--cal-transition);position:relative;overflow:visible}.uc-list-date-header{font-size:14px;font-weight:600;color:var(--cal-text);padding:12px 16px;background:var(--cal-bg-secondary);border-bottom:1px solid var(--cal-border);position:sticky;top:0;z-index:1;overflow:visible}.uc-list-event:hover{background:var(--cal-hover)}.uc-list-event:last-child{border-bottom:none}.uc-list-event-indicator{width:8px;height:8px;border-radius:50%;margin-top:4px;flex-shrink:0}.uc-list-event-time{font-size:13px;font-weight:500;color:var(--cal-text-secondary);min-width:80px;flex-shrink:0}.uc-list-event-content{flex:1;min-width:0}.uc-list-event-title{font-size:14px;font-weight:500;color:var(--cal-text);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.uc-calendar [data-tooltip]:not([data-tooltip=""]):hover::after,.uc-calendar [data-tooltip]:not([data-tooltip=""]):hover::before{opacity:1;visibility:visible;transition-delay:0.4s}.uc-calendar [data-tooltip]:not([data-tooltip=""])::before{content:attr(data-tooltip);position:absolute;bottom:calc(100% + var(--cal-tooltip-offset));left:50%;transform:translateX(-50%);background:var(--cal-tooltip-bg);color:var(--cal-tooltip-text);border:1px solid var(--cal-tooltip-border);box-shadow:var(--cal-tooltip-shadow);padding:var(--cal-tooltip-padding);border-radius:var(--cal-tooltip-radius);font-size:var(--cal-tooltip-font-size);font-weight:500;line-height:1.4;max-width:var(--cal-tooltip-max-width);width:max-content;white-space:pre-wrap;word-wrap:break-word;text-align:left;z-index:1000;pointer-events:none;opacity:0;visibility:hidden;transition:opacity .2s ease,visibility .2s ease}.uc-calendar [data-tooltip]:not([data-tooltip=""])::after{content:'';position:absolute;bottom:calc(100% + var(--cal-tooltip-offset) - 5px);left:50%;transform:translateX(-50%);width:0;height:0;border-left:5px solid transparent;border-right:5px solid transparent;border-top:5px solid var(--cal-tooltip-bg);z-index:1002;pointer-events:none;opacity:0;visibility:hidden;transition:opacity .2s ease,visibility .2s ease}.uc-calendar [data-tooltip]:not([data-tooltip=""])::before{filter:drop-shadow(0 1px 0 var(--cal-tooltip-border))}.uc-calendar [data-tooltip].uc-tooltip-left::before{left:auto;right:0;transform:translateX(0)}.uc-calendar [data-tooltip].uc-tooltip-left::after{left:auto;right:12px;transform:translateX(0)}.uc-calendar [data-tooltip].uc-tooltip-right::before{left:0;transform:translateX(0)}.uc-calendar [data-tooltip].uc-tooltip-right::after{left:12px;transform:translateX(0)}.uc-calendar [data-tooltip].uc-tooltip-bottom::before{bottom:auto;top:calc(100% + var(--cal-tooltip-offset));filter:drop-shadow(0 -1px 0 var(--cal-tooltip-border))}.uc-calendar [data-tooltip].uc-tooltip-bottom::after{bottom:auto;top:calc(100% + var(--cal-tooltip-offset) - 5px);border-top:5px solid var(--cal-tooltip-bg);border-left:5px solid transparent;border-right:5px solid transparent;border-bottom:none;transform:translateX(-50%) rotate(180deg)}@media (max-width:768px){.uc-toolbar{padding:8px 10px}.uc-title{font-size:14px}.uc-toolbar-center{order:-1;width:100%;flex:none}.uc-toolbar-section{justify-content:center}.uc-view-btn{padding:5px 8px;font-size:12px}:root{--cal-time-col-width:52px;--cal-hour-height:52px;--cal-cell-min-height:88px;--cal-event-height:20px}.uc-week-day-num{width:24px;height:24px;font-size:13px}.uc-week-day-name{font-size:10px}.uc-hour-cell:nth-child(odd) .uc-hour-label{visibility:hidden}}@media (max-width:480px){.uc-today-btn{display:none}.uc-toolbar-section.uc-toolbar-right{gap:2px}}.uc-calendar.uc-no-grid .uc-day-cell,.uc-calendar.uc-no-grid .uc-day-header-row,.uc-calendar.uc-no-grid .uc-day-name-row,.uc-calendar.uc-no-grid .uc-grid-line,.uc-calendar.uc-no-grid .uc-hour-cell,.uc-calendar.uc-no-grid .uc-time-col,.uc-calendar.uc-no-grid .uc-time-gutter,.uc-calendar.uc-no-grid .uc-week-day-header,.uc-calendar.uc-no-grid .uc-week-row{border:none!important}.uc-calendar.uc-no-grid .uc-all-day-section{border-top:none!important;border-left:none!important;border-right:none!important}.uc-calendar.uc-no-border{border:none!important}.uc-calendar.uc-dark,.uc-dark .uc-calendar{--cal-bg:#1f2937;--cal-bg-secondary:#111827;--cal-text:#f9fafb;--cal-text-subtle:#9ca3af;--cal-text-muted:#6b7280;--cal-border:#374151;--cal-border-strong:#4b5563;--cal-hover:#374151;--cal-hover-strong:#4b5563;--cal-selected-bg:#78716c;--cal-today-bg:#57534e;--cal-primary-light:#57534e;--cal-toolbar-bg:#111827;--cal-tooltip-bg:#374151;--cal-tooltip-text:#f9fafb;--cal-tooltip-border:#4b5563;--cal-loading-bg:rgba(31, 41, 55, 0.7)}.uc-calendar.uc-dragging{cursor:grabbing!important;user-select:none;-webkit-user-select:none}.uc-calendar.uc-dragging *{cursor:grabbing!important}.uc-dragging-element{opacity:.8;box-shadow:0 4px 12px rgba(0,0,0,.3);transition:none!important}.uc-calendar[data-drag-enabled] .uc-event-bar,.uc-calendar[data-drag-enabled] .uc-timed-event{cursor:grab}.uc-calendar[data-drag-enabled] .uc-event-bar:active,.uc-calendar[data-drag-enabled] .uc-timed-event:active{cursor:grabbing}.uc-calendar.uc-dark .uc-dragging-element{box-shadow:0 4px 12px rgba(0,0,0,.6)}.uc-resize-handle{position:absolute;bottom:0;left:0;right:0;height:8px;cursor:ns-resize;display:flex;align-items:center;justify-content:center;opacity:0;transition:opacity .2s}.uc-resize-handle::after{content:'';width:24px;height:3px;background:rgba(255,255,255,.6);border-radius:2px}.uc-timed-event:hover .uc-resize-handle{opacity:1}.uc-calendar.uc-dragging .uc-resize-handle{cursor:ns-resize!important}.uc-resize-handle-right{position:absolute;top:2px;bottom:2px;right:2px;width:6px;cursor:ew-resize;display:flex;align-items:center;justify-content:center;opacity:0;transition:opacity .2s}.uc-resize-handle-right::after{content:'';width:2px;height:12px;background:rgba(255,255,255,.6);border-radius:1px}.uc-event-bar:hover .uc-resize-handle-right{opacity:1}.uc-calendar.uc-dragging .uc-resize-handle-right{cursor:ew-resize!important}.uc-event-bar--list{display:flex;align-items:center;gap:6px;padding:2px 6px;background:0 0!important;overflow:hidden}.uc-event-dot{flex-shrink:0;width:8px;height:8px;border-radius:50%}.uc-event-bar--list .uc-event-time{flex-shrink:0;font-size:11px;font-weight:500;color:var(--cal-text);opacity:.8}.uc-event-bar--list .uc-event-title{flex:1;font-size:12px;color:var(--cal-text);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.uc-calendar[data-drag-enabled] .uc-event-bar--list{cursor:grab}.uc-calendar[data-drag-enabled] .uc-event-bar--list:active{cursor:grabbing}@media print{.uc-toolbar{display:none}.uc-time-body{overflow:visible}.uc-calendar{border:none}}
@@ -1,14 +1,14 @@
1
1
  /**
2
- * SimpleCalendarJs v3.0.4
2
+ * SimpleCalendarJs v3.0.6
3
3
  * A clean, modern, and feature-rich JavaScript calendar component with zero dependencies
4
4
  *
5
5
  * @author Pedro Lopes <simplecalendarjs@gmail.com>
6
6
  * @homepage https://www.simplecalendarjs.com
7
7
  * @license SEE LICENSE IN LICENSE
8
8
  */
9
- !function(t,e){"undefined"!=typeof module&&module.exports?module.exports=e():"function"==typeof define&&define.amd?define([],e):t.SimpleCalendarJs=e()}("undefined"!=typeof globalThis?globalThis:"undefined"!=typeof window?window:this,function(){"use strict";function t(t){const e=new Date(t);return e.setHours(0,0,0,0),e}function e(t){const e=new Date(t);return e.setHours(23,59,59,999),e}function a(t,e){const a=new Date(t);return a.setDate(a.getDate()+e),a}function s(t,e){return t.getFullYear()===e.getFullYear()&&t.getMonth()===e.getMonth()&&t.getDate()===e.getDate()}function n(t){return s(t,new Date)}function i(e,a){return Math.floor((t(a)-t(e))/864e5)}function o(t){return t instanceof Date?t:new Date(t)}function l(e,s){const n=e.getDay(),i=t(a(e,-((n-s+7)%7)));return Array.from({length:7},(t,e)=>a(i,e))}function r(t,e,s){const n=new Date(t,e,1),i=new Date(t,e+1,0),o=(n.getDay()-s+7)%7,l=7*Math.ceil((o+i.getDate())/7),r=a(n,-o);return Array.from({length:l},(t,e)=>a(r,e))}function c(t,e,a){const s={hour:"numeric",hour12:!a};return 0!==t.getMinutes()&&(s.minute="2-digit"),new Intl.DateTimeFormat(e,s).format(t)}function d(t,e,a){return Array.from({length:7},(s,n)=>{const i=new Date(2025,0,5+(e+n)%7);return new Intl.DateTimeFormat(t,{weekday:a}).format(i)})}function h(t){return String(t).replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;").replace(/'/g,"&#39;")}const u={"en-US":{today:"Today",month:"Month",week:"Week",day:"Day",list:"List",allDay:"All-Day"},"en-GB":{today:"Today",month:"Month",week:"Week",day:"Day",list:"List",allDay:"All-Day"},"es-ES":{today:"Hoy",month:"Mes",week:"Semana",day:"Día",list:"Lista",allDay:"Todo el día"},"es-MX":{today:"Hoy",month:"Mes",week:"Semana",day:"Día",list:"Lista",allDay:"Todo el día"},"fr-FR":{today:"Aujourd'hui",month:"Mois",week:"Semaine",day:"Jour",list:"Liste",allDay:"Toute la journée"},"fr-CA":{today:"Aujourd'hui",month:"Mois",week:"Semaine",day:"Jour",list:"Liste",allDay:"Toute la journée"},"de-DE":{today:"Heute",month:"Monat",week:"Woche",day:"Tag",list:"Liste",allDay:"Ganztägig"},"it-IT":{today:"Oggi",month:"Mese",week:"Settimana",day:"Giorno",list:"Elenco",allDay:"Tutto il giorno"},"pt-PT":{today:"Hoje",month:"Mês",week:"Semana",day:"Dia",list:"Lista",allDay:"Dia todo"},"pt-BR":{today:"Hoje",month:"Mês",week:"Semana",day:"Dia",list:"Lista",allDay:"Dia todo"},"nl-NL":{today:"Vandaag",month:"Maand",week:"Week",day:"Dag",list:"Lijst",allDay:"Hele dag"},"pl-PL":{today:"Dzisiaj",month:"Miesiąc",week:"Tydzień",day:"Dzień",list:"Lista",allDay:"Cały dzień"},"ru-RU":{today:"Сегодня",month:"Месяц",week:"Неделя",day:"День",list:"Список",allDay:"Весь день"},"tr-TR":{today:"Bugün",month:"Ay",week:"Hafta",day:"Gün",list:"Liste",allDay:"Tüm gün"},"sv-SE":{today:"Idag",month:"Månad",week:"Vecka",day:"Dag",list:"Lista",allDay:"Heldag"},"da-DK":{today:"I dag",month:"Måned",week:"Uge",day:"Dag",list:"Liste",allDay:"Hele dagen"},"fi-FI":{today:"Tänään",month:"Kuukausi",week:"Viikko",day:"Päivä",list:"Lista",allDay:"Koko päivä"},"no-NO":{today:"I dag",month:"Måned",week:"Uke",day:"Dag",list:"Liste",allDay:"Hele dagen"},"cs-CZ":{today:"Dnes",month:"Měsíc",week:"Týden",day:"Den",list:"Seznam",allDay:"Celý den"},"hu-HU":{today:"Ma",month:"Hónap",week:"Hét",day:"Nap",list:"Lista",allDay:"Egész nap"},"ro-RO":{today:"Astăzi",month:"Lună",week:"Săptămână",day:"Zi",list:"Listă",allDay:"Toată ziua"},"el-GR":{today:"Σήμερα",month:"Μήνας",week:"Εβδομάδα",day:"Ημέρα",list:"Λίστα",allDay:"Ολοήμερο"},"ja-JP":{today:"今日",month:"月",week:"週",day:"日",list:"リスト",allDay:"終日"},"ko-KR":{today:"오늘",month:"월",week:"주",day:"일",list:"목록",allDay:"종일"},"zh-CN":{today:"今天",month:"月",week:"周",day:"日",list:"列表",allDay:"全天"},"zh-TW":{today:"今天",month:"月",week:"週",day:"日",list:"列表",allDay:"全天"},"ar-SA":{today:"اليوم",month:"شهر",week:"أسبوع",day:"يوم",list:"قائمة",allDay:"طوال اليوم"},"he-IL":{today:"היום",month:"חודש",week:"שבוע",day:"יום",list:"רשימה",allDay:"כל היום"},"hi-IN":{today:"आज",month:"महीना",week:"सप्ताह",day:"दिन",list:"सूची",allDay:"पूरे दिन"},"th-TH":{today:"วันนี้",month:"เดือน",week:"สัปดาห์",day:"วัน",list:"รายการ",allDay:"ตลอดวัน"},"vi-VN":{today:"Hôm nay",month:"Tháng",week:"Tuần",day:"Ngày",list:"Danh sách",allDay:"Cả ngày"},"id-ID":{today:"Hari ini",month:"Bulan",week:"Minggu",day:"Hari",list:"Daftar",allDay:"Sepanjang hari"},"ms-MY":{today:"Hari ini",month:"Bulan",week:"Minggu",day:"Hari",list:"Senarai",allDay:"Sepanjang hari"},"uk-UA":{today:"Сьогодні",month:"Місяць",week:"Тиждень",day:"День",list:"Список",allDay:"Весь день"}};function v(t,e){const a=t.split("-")[0];return(u[t]||u[a]||u["en-US"])[e]}function p(t){return!!t.allDay||!s(t.start,t.end)}function y(a,n){const o=t(n[0]),l=e(n[n.length-1]),r=a.filter(t=>t.start<=l&&t.end>=o).map(a=>({...a,_visStart:new Date(Math.max(a.start.getTime(),o.getTime())),_visEnd:new Date(Math.min(a.end.getTime(),l.getTime())),_isStart:t(a.start)>=o,_isEnd:e(a.end)<=l}));r.sort((t,e)=>{const a=t.start-e.start;if(0!==a)return a;const s=i(t._visStart,t._visEnd);return i(e._visStart,e._visEnd)-s||(t._origStart||t.start)-(e._origStart||e.start)});const c=[],d=[];for(const t of r){const e=n.findIndex(e=>s(e,t._visStart)),a=n.findIndex(e=>s(e,t._visEnd)),i=-1===e?0:e,o=-1===a?n.length-1:a;let l=c.findIndex(t=>t<=i);-1===l?(l=c.length,c.push(o+1)):c[l]=o+1,d.push({event:t,startCol:i,endCol:o,slot:l,isStart:t._isStart,isEnd:t._isEnd})}return d}
9
+ !function(t,e){"undefined"!=typeof module&&module.exports?module.exports=e():"function"==typeof define&&define.amd?define([],e):t.SimpleCalendarJs=e()}("undefined"!=typeof globalThis?globalThis:"undefined"!=typeof window?window:this,function(){"use strict";function t(t){const e=new Date(t);return e.setHours(0,0,0,0),e}function e(t){const e=new Date(t);return e.setHours(23,59,59,999),e}function a(t,e){const a=new Date(t);return a.setDate(a.getDate()+e),a}function s(t,e){return t.getFullYear()===e.getFullYear()&&t.getMonth()===e.getMonth()&&t.getDate()===e.getDate()}function n(t){return s(t,new Date)}function i(e,a){return Math.floor((t(a)-t(e))/864e5)}function o(t){return t instanceof Date?t:new Date(t)}function r(e,s){const n=e.getDay(),i=t(a(e,-((n-s+7)%7)));return Array.from({length:7},(t,e)=>a(i,e))}function l(t,e,s){const n=new Date(t,e,1),i=new Date(t,e+1,0),o=(n.getDay()-s+7)%7,r=7*Math.ceil((o+i.getDate())/7),l=a(n,-o);return Array.from({length:r},(t,e)=>a(l,e))}function d(t,e,a){const s={hour:"numeric",hour12:!a};return 0!==t.getMinutes()&&(s.minute="2-digit"),new Intl.DateTimeFormat(e,s).format(t)}function c(t,e,a){return Array.from({length:7},(s,n)=>{const i=new Date(2025,0,5+(e+n)%7);return new Intl.DateTimeFormat(t,{weekday:a}).format(i)})}function h(t){return String(t).replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;").replace(/'/g,"&#39;")}const u={"en-US":{today:"Today",month:"Month",week:"Week",day:"Day",list:"List",allDay:"All-Day"},"en-GB":{today:"Today",month:"Month",week:"Week",day:"Day",list:"List",allDay:"All-Day"},"es-ES":{today:"Hoy",month:"Mes",week:"Semana",day:"Día",list:"Lista",allDay:"Todo el día"},"es-MX":{today:"Hoy",month:"Mes",week:"Semana",day:"Día",list:"Lista",allDay:"Todo el día"},"fr-FR":{today:"Aujourd'hui",month:"Mois",week:"Semaine",day:"Jour",list:"Liste",allDay:"Toute la journée"},"fr-CA":{today:"Aujourd'hui",month:"Mois",week:"Semaine",day:"Jour",list:"Liste",allDay:"Toute la journée"},"de-DE":{today:"Heute",month:"Monat",week:"Woche",day:"Tag",list:"Liste",allDay:"Ganztägig"},"it-IT":{today:"Oggi",month:"Mese",week:"Settimana",day:"Giorno",list:"Elenco",allDay:"Tutto il giorno"},"pt-PT":{today:"Hoje",month:"Mês",week:"Semana",day:"Dia",list:"Lista",allDay:"Dia todo"},"pt-BR":{today:"Hoje",month:"Mês",week:"Semana",day:"Dia",list:"Lista",allDay:"Dia todo"},"nl-NL":{today:"Vandaag",month:"Maand",week:"Week",day:"Dag",list:"Lijst",allDay:"Hele dag"},"pl-PL":{today:"Dzisiaj",month:"Miesiąc",week:"Tydzień",day:"Dzień",list:"Lista",allDay:"Cały dzień"},"ru-RU":{today:"Сегодня",month:"Месяц",week:"Неделя",day:"День",list:"Список",allDay:"Весь день"},"tr-TR":{today:"Bugün",month:"Ay",week:"Hafta",day:"Gün",list:"Liste",allDay:"Tüm gün"},"sv-SE":{today:"Idag",month:"Månad",week:"Vecka",day:"Dag",list:"Lista",allDay:"Heldag"},"da-DK":{today:"I dag",month:"Måned",week:"Uge",day:"Dag",list:"Liste",allDay:"Hele dagen"},"fi-FI":{today:"Tänään",month:"Kuukausi",week:"Viikko",day:"Päivä",list:"Lista",allDay:"Koko päivä"},"no-NO":{today:"I dag",month:"Måned",week:"Uke",day:"Dag",list:"Liste",allDay:"Hele dagen"},"cs-CZ":{today:"Dnes",month:"Měsíc",week:"Týden",day:"Den",list:"Seznam",allDay:"Celý den"},"hu-HU":{today:"Ma",month:"Hónap",week:"Hét",day:"Nap",list:"Lista",allDay:"Egész nap"},"ro-RO":{today:"Astăzi",month:"Lună",week:"Săptămână",day:"Zi",list:"Listă",allDay:"Toată ziua"},"el-GR":{today:"Σήμερα",month:"Μήνας",week:"Εβδομάδα",day:"Ημέρα",list:"Λίστα",allDay:"Ολοήμερο"},"ja-JP":{today:"今日",month:"月",week:"週",day:"日",list:"リスト",allDay:"終日"},"ko-KR":{today:"오늘",month:"월",week:"주",day:"일",list:"목록",allDay:"종일"},"zh-CN":{today:"今天",month:"月",week:"周",day:"日",list:"列表",allDay:"全天"},"zh-TW":{today:"今天",month:"月",week:"週",day:"日",list:"列表",allDay:"全天"},"ar-SA":{today:"اليوم",month:"شهر",week:"أسبوع",day:"يوم",list:"قائمة",allDay:"طوال اليوم"},"he-IL":{today:"היום",month:"חודש",week:"שבוע",day:"יום",list:"רשימה",allDay:"כל היום"},"hi-IN":{today:"आज",month:"महीना",week:"सप्ताह",day:"दिन",list:"सूची",allDay:"पूरे दिन"},"th-TH":{today:"วันนี้",month:"เดือน",week:"สัปดาห์",day:"วัน",list:"รายการ",allDay:"ตลอดวัน"},"vi-VN":{today:"Hôm nay",month:"Tháng",week:"Tuần",day:"Ngày",list:"Danh sách",allDay:"Cả ngày"},"id-ID":{today:"Hari ini",month:"Bulan",week:"Minggu",day:"Hari",list:"Daftar",allDay:"Sepanjang hari"},"ms-MY":{today:"Hari ini",month:"Bulan",week:"Minggu",day:"Hari",list:"Senarai",allDay:"Sepanjang hari"},"uk-UA":{today:"Сьогодні",month:"Місяць",week:"Тиждень",day:"День",list:"Список",allDay:"Весь день"}};function _(t,e){const a=t.split("-")[0];return(u[t]||u[a]||u["en-US"])[e]}function v(t){return!!t.allDay||!s(t.start,t.end)}function g(a,n){const o=t(n[0]),r=e(n[n.length-1]),l=a.filter(t=>t.start<=r&&t.end>=o).map(a=>({...a,_visStart:new Date(Math.max(a.start.getTime(),o.getTime())),_visEnd:new Date(Math.min(a.end.getTime(),r.getTime())),_isStart:t(a.start)>=o,_isEnd:e(a.end)<=r}));l.sort((t,e)=>{const a=t.start-e.start;if(0!==a)return a;const s=i(t._visStart,t._visEnd);return i(e._visStart,e._visEnd)-s||(t._origStart||t.start)-(e._origStart||e.start)});const d=[],c=[];for(const t of l){const e=n.findIndex(e=>s(e,t._visStart)),a=n.findIndex(e=>s(e,t._visEnd)),i=-1===e?0:e,o=-1===a?n.length-1:a;let r=d.findIndex(t=>t<=i);-1===r?(r=d.length,d.push(o+1)):d[r]=o+1,c.push({event:t,startCol:i,endCol:o,slot:r,isStart:t._isStart,isEnd:t._isEnd})}return c}
10
10
  /* ============================================================
11
11
  ES MODULE EXPORT
12
12
  Also available as window.SimpleCalendarJs via the IIFE wrapper.
13
13
  ============================================================ */
14
- return class{constructor(e,a={}){if("string"==typeof e){if(this._el=document.querySelector(e),!this._el)throw new Error(`SimpleCalendarJs: no element for "${e}"`)}else this._el=e;this._opts=Object.assign({defaultView:"month",defaultDate:null,weekStartsOn:0,locale:"default",weekdayFormat:"short",use24Hour:!1,showTimeInItems:!0,showGridLines:!0,showToolbar:!0,showTodayButton:!0,showNavigation:!0,showTitle:!0,showYearPicker:!0,showViewSwitcher:!0,showTooltips:!0,showBorder:!0,maxEventsPerCell:3,listDaysForward:30,enabledViews:["month","week","day"],fetchEvents:null,onEventClick:null,onSlotClick:null,onViewChange:null,onNavigate:null},a),this._view=this._opts.defaultView,this._date=t(this._opts.defaultDate||new Date),this._events=[],this._cachedRange=null,this._cachedEvents=[],this._nowInterval=null,this._yearPickerOpen=!1,this._yearPickerBase=0,this._yearOutsideHandler=null,this._root=document.createElement("div"),this._root.className="uc-calendar",this._opts.showGridLines||this._root.classList.add("uc-no-grid"),this._opts.showBorder||this._root.classList.add("uc-no-border"),this._el.appendChild(this._root),this._onClick=this._handleClick.bind(this),this._root.addEventListener("click",this._onClick),this._onMouseMove=this._handleTooltipPosition.bind(this),this._root.addEventListener("mouseover",this._onMouseMove),this._fetchAndRender(),this._startNowUpdater()}setView(t){t!==this._view&&(this._view=t,this._opts.onViewChange&&this._opts.onViewChange(t),this._fetchAndRender())}navigate(t){const e=new Date(this._date);"month"===this._view?(e.setMonth(e.getMonth()+t),e.setDate(1)):"week"===this._view?e.setDate(e.getDate()+7*t):e.setDate(e.getDate()+t),this._date=e;const a=this._getRange();this._opts.onNavigate&&this._opts.onNavigate(a.start,a.end),this._fetchAndRender()}goToToday(){this._date=t(new Date);const e=this._getRange();this._opts.onNavigate&&this._opts.onNavigate(e.start,e.end),this._fetchAndRender()}goToDate(e){this._date=t(o(e)),this._fetchAndRender()}refresh(){this._cachedRange=null,this._cachedEvents=[],this._fetchAndRender()}destroy(){this._nowInterval&&clearInterval(this._nowInterval),this._yearOutsideHandler&&document.removeEventListener("click",this._yearOutsideHandler),this._root.removeEventListener("click",this._onClick),this._root.removeEventListener("mouseover",this._onMouseMove),this._root.remove()}_getRange(){if("month"===this._view){const a=r(this._date.getFullYear(),this._date.getMonth(),this._opts.weekStartsOn);return{start:t(a[0]),end:e(a[a.length-1])}}if("week"===this._view){const a=l(this._date,this._opts.weekStartsOn);return{start:t(a[0]),end:e(a[6])}}if("list"===this._view){const t=new Date,e=new Date(t);return e.setDate(e.getDate()+this._opts.listDaysForward),{start:t,end:e}}return{start:t(this._date),end:e(this._date)}}_getMonthGridRange(){const a=r(this._date.getFullYear(),this._date.getMonth(),this._opts.weekStartsOn);return{start:t(a[0]),end:e(a[a.length-1])}}async _fetchAndRender(){if(this._root.innerHTML=this._buildShell(),!this._opts.fetchEvents)return void this._renderView();const t=this._getRange();if(this._cachedRange&&this._cachedRange.start<=t.start&&this._cachedRange.end>=t.end)return this._events=this._cachedEvents.filter(e=>e.end>=t.start&&e.start<=t.end),void this._renderView();const e=this._root.querySelector(".uc-loading");e&&(e.style.display="flex");try{let e;e="list"===this._view?t:this._getMonthGridRange();const a=(await this._opts.fetchEvents(e.start,e.end)||[]).map(t=>({...t,start:o(t.start),end:o(t.end)}));this._cachedRange={start:e.start,end:e.end},this._cachedEvents=a,this._events=a.filter(e=>e.end>=t.start&&e.start<=t.end)}catch(t){this._events=[]}e&&(e.style.display="none"),this._renderView()}_renderView(){const t=this._root.querySelector(".uc-view-container");if(t){if(0===this._opts.maxEventsPerCell)this._root.classList.add("uc-unlimited-events"),this._root.style.removeProperty("--cal-cell-min-height");else{this._root.classList.remove("uc-unlimited-events");const t=30+24*this._opts.maxEventsPerCell+28;this._root.style.setProperty("--cal-cell-min-height",`${t}px`)}if("month"===this._view)t.innerHTML=this._buildMonthView();else if("week"===this._view){const e=l(this._date,this._opts.weekStartsOn);t.innerHTML=this._buildWeekOrDayView(e),this._scrollToBusinessHours(t)}else"list"===this._view?t.innerHTML=this._buildListView():(t.innerHTML=this._buildWeekOrDayView([this._date]),this._scrollToBusinessHours(t))}}_scrollToBusinessHours(t){requestAnimationFrame(()=>{const e=t.querySelector(".uc-time-body");if(!e)return;const a=parseFloat(getComputedStyle(this._root).getPropertyValue("--cal-hour-height"))||60;e.scrollTop=7*a})}_renderToolbar(){const t=this._root.querySelector(".uc-toolbar");if(!t)return;const e=document.createElement("div");e.innerHTML=this._buildToolbar(),this._root.replaceChild(e.firstElementChild,t)}_buildShell(){return`\n ${this._buildToolbar()}\n <div class="uc-loading" style="display:none">\n <div class="uc-spinner"></div>\n </div>\n <div class="uc-view-container"></div>\n `}_buildToolbar(){if(!this._opts.showToolbar)return"";const t=this._date.getFullYear(),e=(new Date).getFullYear();let a,n=!0;if("month"===this._view)a=new Intl.DateTimeFormat(this._opts.locale,{month:"long"}).format(this._date);else if("week"===this._view){const t=l(this._date,this._opts.weekStartsOn);a=function(t,e,a){if(t.getMonth()===e.getMonth()&&t.getFullYear()===e.getFullYear())return`${new Intl.DateTimeFormat(a,{month:"long"}).format(t)} ${t.getDate()}–${e.getDate()}`;const s=t=>new Intl.DateTimeFormat(a,{month:"short",day:"numeric"}).format(t);return`${s(t)} – ${s(e)}`}(t[0],t[6],this._opts.locale)}else if("list"===this._view){const t=new Date,e=new Date(t);e.setDate(e.getDate()+this._opts.listDaysForward),a=function(t,e,a){if(t.getMonth()===e.getMonth()&&t.getFullYear()===e.getFullYear())return`${new Intl.DateTimeFormat(a,{month:"long"}).format(t)} ${t.getDate()}–${e.getDate()}, ${t.getFullYear()}`;const s=t=>new Intl.DateTimeFormat(a,{month:"short",day:"numeric"}).format(t);return`${s(t)} – ${s(e)}, ${e.getFullYear()}`}(t,e,this._opts.locale),n=!1}else a=new Intl.DateTimeFormat(this._opts.locale,{weekday:"long",month:"long",day:"numeric"}).format(this._date);let i="";if(this._opts.showYearPicker&&this._yearPickerOpen){const a=this._yearPickerBase,s=Array.from({length:12},(s,n)=>{const i=a+n,o=i===t;return`<button class="${"uc-year-item"+(o?" uc-active":"")+(i===e&&!o?" uc-today-year":"")}" data-action="select-year" data-year="${i}">${i}</button>`}).join("");i=`\n <div class="uc-year-picker">\n <div class="uc-year-picker-nav">\n <button class="uc-year-nav-btn" data-action="year-prev" aria-label="Previous years">&#8249;</button>\n <span class="uc-year-range">${a} – ${a+11}</span>\n <button class="uc-year-nav-btn" data-action="year-next" aria-label="Next years">&#8250;</button>\n </div>\n <div class="uc-year-grid">${s}</div>\n </div>`}const o=this._opts.locale,r=v(o,"today"),c=v(o,"month"),d=v(o,"week"),u=v(o,"day"),p=v(o,"list");let y="";if((this._opts.showNavigation||this._opts.showTodayButton)&&"list"!==this._view){const t=this._opts.showNavigation?'<button class="uc-btn uc-nav-btn" data-action="prev" aria-label="Previous">&#8249;</button>':"",e=new Date,a=s(this._date,e)?" uc-active":"";y=`\n <div class="uc-toolbar-section uc-toolbar-left">\n ${t}${this._opts.showTodayButton?`<button class="uc-btn uc-today-btn${a}" data-action="today">${h(r)}</button>`:""}${this._opts.showNavigation?'<button class="uc-btn uc-nav-btn" data-action="next" aria-label="Next">&#8250;</button>':""}\n </div>`}let g="";if(this._opts.showTitle){const e=n?this._opts.showYearPicker?`<button class="uc-year-btn${this._yearPickerOpen?" uc-open":""}" data-action="year-pick" aria-label="Select year">${t}</button>`:t:"";g=`\n <div class="uc-toolbar-section uc-toolbar-center">\n <h2 class="uc-title">\n <span class="uc-title-main">${h(a)}</span>\n ${e}\n </h2>\n ${i}\n </div>`}let _="";if(this._opts.showViewSwitcher){const t=this._opts.enabledViews,e=[];t.includes("month")&&e.push(`<button class="uc-btn uc-view-btn${"month"===this._view?" uc-active":""}" data-view="month">${h(c)}</button>`),t.includes("week")&&e.push(`<button class="uc-btn uc-view-btn${"week"===this._view?" uc-active":""}" data-view="week">${h(d)}</button>`),t.includes("day")&&e.push(`<button class="uc-btn uc-view-btn${"day"===this._view?" uc-active":""}" data-view="day">${h(u)}</button>`),t.includes("list")&&e.push(`<button class="uc-btn uc-view-btn${"list"===this._view?" uc-active":""}" data-view="list">${h(p)}</button>`),e.length>0&&(_=`\n <div class="uc-toolbar-section uc-toolbar-right">\n <div class="uc-view-switcher">\n ${e.join("")}\n </div>\n </div>`)}return`\n <div class="uc-toolbar">\n ${y}${g}${_}\n </div>\n `}_buildMonthView(){const{locale:a,weekStartsOn:s}=this._opts,n=r(this._date.getFullYear(),this._date.getMonth(),s),i=d(a,s,this._opts.weekdayFormat),o=this._events.map(a=>({...a,_origStart:a.start,start:p(a)?t(a.start):a.start,end:p(a)?e(a.end):a.end})),l=i.map(t=>`<div class="uc-month-day-name">${h(t)}</div>`).join(""),c=[];for(let t=0;t<n.length;t+=7)c.push(n.slice(t,t+7));return`\n <div class="uc-month-view">\n <div class="uc-month-header">${l}</div>\n <div class="uc-month-body">${c.map(t=>this._buildWeekRow(t,o)).join("")}</div>\n </div>\n `}_buildWeekRow(t,e){const a=this._date.getMonth(),i=0===this._opts.maxEventsPerCell?1/0:this._opts.maxEventsPerCell,o=e.filter(p),l=e.filter(t=>!p(t)),r=y(o,t),d=Array.from({length:7},()=>new Set);for(const{startCol:t,endCol:e,slot:a}of r)for(let s=t;s<=e;s++)d[s].add(a);const u=t.map(t=>l.filter(e=>s(e.start,t)).sort((t,e)=>t.start-e.start)),v=t.map((t,e)=>`\n <div class="uc-day-cell${n(t)?" uc-today":""}${t.getMonth()!==a?" uc-other-month":""}" data-date="${t.toISOString()}" data-action="day-click">\n <span class="uc-day-number" data-action="day-number" data-date="${t.toISOString()}">${t.getDate()}</span>\n </div>`).join("");let g="";for(const{event:t,startCol:e,endCol:a,slot:s,isStart:n,isEnd:o}of r){if(i!==1/0&&s>=i)continue;const l=100/7,r=e*l,c=(a-e+1)*l,d=`calc(var(--cal-header-day-height) + ${s} * (var(--cal-event-height) + var(--cal-event-gap)) + 4px)`,u=t.color||"var(--cal-event-bg)",v=n?"var(--cal-event-border-radius)":"0",p=o?"var(--cal-event-border-radius)":"0",y=n?"":" uc-continues-left",_=o?"":" uc-continues-right",w=this._opts.showTooltips?`data-tooltip="${h(t.tooltip||t.description||t.title)}"`:"";g+=`\n <div class="uc-event-bar${y}${_}"\n style="left:calc(${r}% + 2px);width:calc(${c}% - 4px);top:${d};background:${h(u)};border-radius:${v} ${p} ${p} ${v};"\n data-event-id="${h(t.id)}" data-action="event-click"\n ${w}>\n ${n?`<span class="uc-event-title">${h(t.title)}</span>`:"&nbsp;"}\n </div>`}let _=-1;for(let e=0;e<7;e++){const a=100/7,s=e*a,n=t[e],o=[...d[e]],l=o.length>0?Math.max(...o)+1:0,r=u[e];if(i===1/0)r.forEach((t,e)=>{const n=l+e;_=Math.max(_,n);const i=`calc(var(--cal-header-day-height) + ${n} * (var(--cal-event-height) + var(--cal-event-gap)) + 4px)`,o=t.color||"var(--cal-event-bg)",r=c(t.start,this._opts.locale,this._opts.use24Hour),d=this._opts.showTimeInItems?`<span class="uc-event-time">${h(r)}</span>`:"",u=this._opts.showTooltips?`data-tooltip="${h(t.tooltip||t.description||t.title)}"`:"";g+=`\n <div class="uc-event-bar"\n style="left:calc(${s}% + 2px);width:calc(${a}% - 4px);top:${i};background:${h(o)};"\n data-event-id="${h(t.id)}" data-action="event-click"\n ${u}>\n ${d}\n <span class="uc-event-title">${h(t.title)}</span>\n </div>`});else{const t=[...d[e]].filter(t=>t>=i).length,o=[];for(let t=l;t<i;t++)o.push(t);let u=t;if(r.forEach((t,e)=>{if(e<o.length){const n=o[e];_=Math.max(_,n);const i=`calc(var(--cal-header-day-height) + ${n} * (var(--cal-event-height) + var(--cal-event-gap)) + 4px)`,l=t.color||"var(--cal-event-bg)",r=c(t.start,this._opts.locale,this._opts.use24Hour),d=this._opts.showTimeInItems?`<span class="uc-event-time">${h(r)}</span>`:"",u=this._opts.showTooltips?`data-tooltip="${h(t.tooltip||t.description||t.title)}"`:"";g+=`\n <div class="uc-event-bar"\n style="left:calc(${s}% + 2px);width:calc(${a}% - 4px);top:${i};background:${h(l)};"\n data-event-id="${h(t.id)}" data-action="event-click"\n ${u}>\n ${d}\n <span class="uc-event-title">${h(t.title)}</span>\n </div>`}else u++}),u>0){g+=`\n <div class="uc-more-link"\n style="left:calc(${s}% + 2px);width:calc(${a}% - 4px);top:${`calc(var(--cal-header-day-height) + ${i} * (var(--cal-event-height) + var(--cal-event-gap)) + 2px)`};"\n data-date="${n.toISOString()}" data-action="more-click">\n +${u} more\n </div>`}}}for(const{slot:t}of r)(i===1/0||t<i)&&(_=Math.max(_,t));let w="";return i===1/0&&(w=_>=0?` style="min-height: calc(var(--cal-header-day-height) + ${_+1} * (var(--cal-event-height) + var(--cal-event-gap)) + 8px);"`:' style="min-height: 80px;"'),`\n <div class="uc-week-row"${w}>\n <div class="uc-week-cells">${v}</div>\n <div class="uc-week-events">${g}</div>\n </div>`}_buildWeekOrDayView(a){const{locale:i,weekStartsOn:o,use24Hour:l}=this._opts,r=1===a.length,u=r?" uc-day-header-single":"",g=r?[new Intl.DateTimeFormat(i,{weekday:this._opts.weekdayFormat}).format(a[0])]:d(i,o,this._opts.weekdayFormat),_=t(a[0]),w=e(a[a.length-1]),m=this._events.filter(t=>p(t)&&t.start<=w&&e(t.end)>=_).map(a=>({...a,start:t(a.start),end:e(a.end)})),f=this._events.filter(t=>!p(t)&&t.start>=_&&t.start<=w),$=a.map((t,e)=>{const a=n(t)?" uc-today":"",s=t.getDate();return`\n <div class="uc-week-day-header${a}">\n <span class="uc-week-day-name">${h(g[e])}</span>\n <span class="uc-week-day-num" data-action="day-number" data-date="${t.toISOString()}">${s}</span>\n </div>`}).join(""),k=r?m.map((t,e)=>({event:t,startCol:0,endCol:0,slot:e,isStart:!0,isEnd:!0})):y(m,a),b=k.length?Math.max(...k.map(t=>t.slot))+1:0;let D="";for(const{event:t,startCol:e,endCol:s,slot:n,isStart:i,isEnd:o}of k){const l=100/a.length,r=e*l,c=(s-e+1)*l,d=`calc(${n} * (var(--cal-event-height) + 3px) + 2px)`,u=t.color||"var(--cal-event-bg)",v=i?"var(--cal-event-border-radius)":"0",p=o?"var(--cal-event-border-radius)":"0",y=i?"":" uc-continues-left",g=o?"":" uc-continues-right",_=this._opts.showTooltips?`data-tooltip="${h(t.tooltip||t.description||t.title)}"`:"";D+=`\n <div class="uc-event-bar${y}${g}"\n style="left:calc(${r}% + 2px);width:calc(${c}% - 4px);top:${d};background:${h(u)};border-radius:${v} ${p} ${p} ${v};"\n data-event-id="${h(t.id)}" data-action="event-click"\n ${_}>\n ${i?`<span class="uc-event-title">${h(t.title)}</span>`:"&nbsp;"}\n </div>`}const T=`calc(${Math.max(1,b)} * (var(--cal-event-height) + 3px) + 6px)`,S=new Date,M=60*S.getHours()+S.getMinutes(),C=a.length,x=Array.from({length:24},(t,e)=>`<div class="uc-hour-cell"><span class="uc-hour-label">${h(0===e?"":c(new Date(2e3,0,1,e),"en-US",l))}</span></div>`).join(""),H=a.map(t=>{const e=n(t)?" uc-today":"",a=Array.from({length:24},()=>'<div class="uc-hour-row"><div class="uc-half-hour-line"></div></div>').join(""),o=function(t){if(!t.length)return[];const e=[...t].sort((t,e)=>t.start-e.start||e.end-t.end),a=[],s=[];for(const t of e){let e=a.findIndex(e=>e<=t.start);-1===e?(e=a.length,a.push(t.end)):a[e]=t.end,s.push({event:t,col:e})}return s.map(t=>{const e=s.filter(e=>e.event.start<t.event.end&&e.event.end>t.event.start),a=Math.max(...e.map(t=>t.col))+1;return{...t,totalCols:a}})}(f.filter(e=>s(e.start,t))).map(({event:t,col:e,totalCols:a})=>{const s=60*t.start.getHours()+t.start.getMinutes(),n=60*t.end.getHours()+t.end.getMinutes(),o=`calc(${s} / 60 * var(--cal-hour-height))`,r=`calc(${Math.max(n-s,30)} / 60 * var(--cal-hour-height))`,d=100/a,u=`calc(${e*d}% + 1px)`,v=`calc(${d}% - 2px)`,p=t.color||"var(--cal-event-bg)",y=c(t.start,i,l),g=n-s<=60?" uc-timed-event--short":"",_=this._opts.showTimeInItems?`<span class="uc-event-time">${h(y)}</span>`:"",w=this._opts.showTooltips?`data-tooltip="${h(t.tooltip||t.description||t.title)}"`:"";return`\n <div class="uc-timed-event${g}"\n style="top:${o};height:${r};left:${u};width:${v};background:${h(p)};"\n data-event-id="${h(t.id)}" data-action="event-click"\n ${w}>\n ${_}\n <span class="uc-event-title">${h(t.title)}</span>\n </div>`}).join(""),r=n(t)?`<div class="uc-now-indicator" style="top:calc(${M} / 60 * var(--cal-hour-height));">\n <span class="uc-now-dot"></span>\n <span class="uc-now-line"></span>\n </div>`:"";return`<div class="uc-time-col${e}" data-date="${t.toISOString()}" data-action="slot-col">\n ${a}${o}${r}\n </div>`}).join("");return`\n <div class="${r?"uc-day-view":"uc-week-view"}">\n <div class="uc-week-header">\n <div class="uc-time-gutter-spacer"></div>\n <div class="uc-week-day-headers${u}">${$}</div>\n </div>\n <div class="uc-all-day-section">\n <div class="uc-time-gutter-spacer uc-all-day-label">${v(this._opts.locale,"allDay")}</div>\n <div class="uc-all-day-events" style="min-height:${T}">${D}</div>\n </div>\n <div class="uc-time-body">\n <div class="uc-time-grid-inner">\n <div class="uc-time-gutter">${x}</div>\n <div class="uc-time-columns" style="--uc-col-count:${C}">${H}</div>\n </div>\n </div>\n </div>`}_buildListView(){const e=new Date,a=t(e),s=new Date(a);s.setDate(s.getDate()+this._opts.listDaysForward);const n=this._events.filter(t=>t.end>=e&&t.start<=s);n.sort((e,s)=>{const n=(e.start<a?a:t(e.start))-(s.start<a?a:t(s.start));return 0!==n?n:e.start-s.start});const i=new Map;if(n.forEach(e=>{const s=e.start<a?a:t(e.start),n=`${(o=s).getFullYear()}-${String(o.getMonth()+1).padStart(2,"0")}-${String(o.getDate()).padStart(2,"0")}`;var o;i.has(n)||i.set(n,[]),i.get(n).push(e)}),0===i.size)return'\n <div class="uc-list-view">\n <div class="uc-list-empty">\n <p>No upcoming events</p>\n </div>\n </div>';let l="";return i.forEach((t,e)=>{const a=o(e),s=new Intl.DateTimeFormat(this._opts.locale,{weekday:"long",year:"numeric",month:"long",day:"numeric"}).format(a),n=t.map(t=>{const e=p(t)?"All day":c(t.start,this._opts.locale,this._opts.use24Hour),a=t.color||"var(--cal-event-bg)",s=this._opts.showTooltips?`data-tooltip="${h(t.tooltip||t.description||"")}"`:"";return`\n <div class="uc-list-event" data-event-id="${h(t.id)}" data-action="event-click" ${s}>\n <div class="uc-list-event-indicator" style="background: ${h(a)};"></div>\n <div class="uc-list-event-time">${h(e)}</div>\n <div class="uc-list-event-content">\n <div class="uc-list-event-title">${h(t.title)}</div>\n </div>\n </div>`}).join("");l+=`\n <div class="uc-list-date-group">\n <div class="uc-list-date-header">${h(s)}</div>\n <div class="uc-list-events">${n}</div>\n </div>`}),`<div class="uc-list-view">${l}</div>`}_closeYearPicker(){this._yearOutsideHandler&&(document.removeEventListener("click",this._yearOutsideHandler),this._yearOutsideHandler=null),this._yearPickerOpen=!1,this._renderToolbar()}_handleClick(e){const a=e.target.closest("[data-view]");if(a)return void this.setView(a.dataset.view);const s=e.target.closest("[data-action]");if(!s)return;switch(s.dataset.action){case"prev":this.navigate(-1);break;case"next":this.navigate(1);break;case"today":this.goToToday();break;case"year-pick":e.stopPropagation(),this._yearPickerOpen?this._closeYearPicker():(this._yearPickerOpen=!0,this._yearPickerBase=this._date.getFullYear()-4,this._renderToolbar(),this._yearOutsideHandler=t=>{t.target.closest(".uc-year-picker")||t.target.closest('[data-action="year-pick"]')||this._closeYearPicker()},setTimeout(()=>document.addEventListener("click",this._yearOutsideHandler),0));break;case"year-prev":e.stopPropagation(),this._yearPickerBase-=12,this._renderToolbar();break;case"year-next":e.stopPropagation(),this._yearPickerBase+=12,this._renderToolbar();break;case"select-year":{e.stopPropagation();const t=parseInt(s.dataset.year,10);this._date=new Date(this._date.getFullYear()!==t?new Date(this._date).setFullYear(t):this._date),this._closeYearPicker();const a=this._getRange();this._opts.onNavigate&&this._opts.onNavigate(a.start,a.end),this._fetchAndRender();break}case"event-click":{e.stopPropagation();const t=s.dataset.eventId,a=this._events.find(e=>String(e.id)===String(t));a&&this._opts.onEventClick&&this._opts.onEventClick(a,e);break}case"day-click":{if(e.target.closest('[data-action="day-number"]'))break;if(e.target.closest('[data-action="event-click"]'))break;const t=new Date(s.dataset.date);this._opts.onSlotClick&&this._opts.onSlotClick(t,e);break}case"day-number":{if(e.stopPropagation(),!this._opts.enabledViews.includes("day"))break;const a=new Date(s.dataset.date);this._date=t(a),this._view="day",this._opts.onViewChange&&this._opts.onViewChange("day"),this._fetchAndRender();break}case"more-click":{if(e.stopPropagation(),!this._opts.enabledViews.includes("day"))break;const a=new Date(s.dataset.date);this._date=t(a),this._view="day",this._opts.onViewChange&&this._opts.onViewChange("day"),this._fetchAndRender();break}case"slot-col":if(e.target.closest('[data-action="event-click"]'))break;if(this._opts.onSlotClick){const t=s,a=t.getBoundingClientRect(),n=e.clientY-a.top,i=parseFloat(getComputedStyle(this._root).getPropertyValue("--cal-hour-height"))||60,o=Math.max(0,Math.floor(n/i*60)),l=Math.floor(o/60)%24,r=15*Math.round(o%60/15),c=new Date(t.dataset.date);c.setHours(l,r,0,0),this._opts.onSlotClick(c,e)}}}_handleTooltipPosition(t){const e=t.target.closest("[data-tooltip]");if(!e||!e.dataset.tooltip)return;if(!e.dataset.tooltip.trim())return;const a=e.getBoundingClientRect(),s=window.innerWidth;e.classList.remove("uc-tooltip-left","uc-tooltip-right","uc-tooltip-bottom");const n=a.width/2-125;a.left+n+250>s-10?e.classList.add("uc-tooltip-left"):a.left+n<10&&e.classList.add("uc-tooltip-right"),a.top<50&&e.classList.add("uc-tooltip-bottom")}_startNowUpdater(){this._nowInterval=setInterval(()=>{const t=this._root.querySelectorAll(".uc-now-indicator");if(!t.length)return;const e=new Date,a=`calc(${60*e.getHours()+e.getMinutes()} / 60 * var(--cal-hour-height))`;t.forEach(t=>t.style.top=a)},6e4)}}});
14
+ return class{constructor(e,a={}){if("string"==typeof e){if(this._el=document.querySelector(e),!this._el)throw new Error(`SimpleCalendarJs: no element for "${e}"`)}else this._el=e;this._opts=Object.assign({defaultView:"month",defaultDate:null,weekStartsOn:0,locale:"default",weekdayFormat:"short",use24Hour:!1,showTimeInItems:!0,showGridLines:!0,monthTimedEventStyle:"list",showToolbar:!0,showTodayButton:!0,showNavigation:!0,showTitle:!0,showYearPicker:!0,showViewSwitcher:!0,showTooltips:!0,showBorder:!0,maxEventsPerCell:-1,listDaysForward:30,enabledViews:["month","week","day"],enableDragDrop:!1,enableResize:!1,fetchEvents:null,onEventClick:null,onSlotClick:null,onViewChange:null,onNavigate:null,onEventDrop:null},a),this._view=this._opts.defaultView,this._date=t(this._opts.defaultDate||new Date),this._events=[],this._cachedRange=null,this._cachedEvents=[],this._nowInterval=null,this._yearPickerOpen=!1,this._yearPickerBase=0,this._yearOutsideHandler=null,this._dragState=null,this._clickSuppressed=!1,this._root=document.createElement("div"),this._root.className="uc-calendar",this._opts.showGridLines||this._root.classList.add("uc-no-grid"),this._opts.showBorder||this._root.classList.add("uc-no-border"),this._el.appendChild(this._root),this._onClick=this._handleClick.bind(this),this._root.addEventListener("click",this._onClick),this._onMouseMove=this._handleTooltipPosition.bind(this),this._root.addEventListener("mouseover",this._onMouseMove),(this._opts.enableDragDrop||this._opts.enableResize)&&(this._onMouseDown=this._handleDragStart.bind(this),this._onMouseMoveGlobal=this._handleDragMove.bind(this),this._onMouseUp=this._handleDragEnd.bind(this),this._onTouchStart=this._handleDragStart.bind(this),this._onTouchMove=this._handleDragMove.bind(this),this._onTouchEnd=this._handleDragEnd.bind(this),this._onKeyDown=this._handleDragKeyDown.bind(this),this._root.addEventListener("mousedown",this._onMouseDown),document.addEventListener("mousemove",this._onMouseMoveGlobal),document.addEventListener("mouseup",this._onMouseUp),this._root.addEventListener("touchstart",this._onTouchStart,{passive:!0}),document.addEventListener("touchmove",this._onTouchMove,{passive:!1}),document.addEventListener("touchend",this._onTouchEnd),document.addEventListener("keydown",this._onKeyDown),this._opts.enableDragDrop&&this._root.setAttribute("data-drag-enabled","true"),this._opts.enableResize&&this._root.setAttribute("data-resize-enabled","true")),this._root.innerHTML=this._buildShell(),this._fetchAndRender(),this._startNowUpdater()}setView(t){t!==this._view&&(this._view=t,this._opts.onViewChange&&this._opts.onViewChange(t),this._fetchAndRender())}navigate(t){const e=new Date(this._date);"month"===this._view?(e.setMonth(e.getMonth()+t),e.setDate(1)):"week"===this._view?e.setDate(e.getDate()+7*t):e.setDate(e.getDate()+t),this._date=e;const a=this._getRange();this._opts.onNavigate&&this._opts.onNavigate(a.start,a.end),this._fetchAndRender()}goToToday(){this._date=t(new Date);const e=this._getRange();this._opts.onNavigate&&this._opts.onNavigate(e.start,e.end),this._fetchAndRender()}goToDate(e){this._date=t(o(e)),this._fetchAndRender()}refresh(){this._cachedRange=null,this._cachedEvents=[],this._fetchAndRender()}destroy(){this._nowInterval&&clearInterval(this._nowInterval),this._yearOutsideHandler&&document.removeEventListener("click",this._yearOutsideHandler),this._root.removeEventListener("click",this._onClick),this._root.removeEventListener("mouseover",this._onMouseMove),(this._opts.enableDragDrop||this._opts.enableResize)&&(this._root.removeEventListener("mousedown",this._onMouseDown),document.removeEventListener("mousemove",this._onMouseMoveGlobal),document.removeEventListener("mouseup",this._onMouseUp),this._root.removeEventListener("touchstart",this._onTouchStart),document.removeEventListener("touchmove",this._onTouchMove),document.removeEventListener("touchend",this._onTouchEnd),document.removeEventListener("keydown",this._onKeyDown)),this._root.remove()}_getRange(){if("month"===this._view){const a=l(this._date.getFullYear(),this._date.getMonth(),this._opts.weekStartsOn);return{start:t(a[0]),end:e(a[a.length-1])}}if("week"===this._view){const a=r(this._date,this._opts.weekStartsOn);return{start:t(a[0]),end:e(a[6])}}if("list"===this._view){const t=new Date,e=new Date(t);return e.setDate(e.getDate()+this._opts.listDaysForward),{start:t,end:e}}return{start:t(this._date),end:e(this._date)}}_getMonthGridRange(){const a=l(this._date.getFullYear(),this._date.getMonth(),this._opts.weekStartsOn);return{start:t(a[0]),end:e(a[a.length-1])}}async _fetchAndRender(){if(this._updateToolbar(),!this._opts.fetchEvents)return void this._renderView();const t=this._getRange();if(this._cachedRange&&this._cachedRange.start<=t.start&&this._cachedRange.end>=t.end)return this._events=this._cachedEvents.filter(e=>e.end>=t.start&&e.start<=t.end),void this._renderView();const e=this._root.querySelector(".uc-loading");e&&(e.style.display="flex");try{let e;e="list"===this._view?t:this._getMonthGridRange();const a=(await this._opts.fetchEvents(e.start,e.end)||[]).map(t=>({...t,start:o(t.start),end:o(t.end)}));this._cachedRange={start:e.start,end:e.end},this._cachedEvents=a,this._events=a.filter(e=>e.end>=t.start&&e.start<=t.end)}catch(t){this._events=[]}e&&(e.style.display="none"),this._renderView()}_renderView(){const t=this._root.querySelector(".uc-view-container");if(t){if(0===this._opts.maxEventsPerCell)this._root.classList.add("uc-unlimited-events"),this._root.style.removeProperty("--cal-cell-min-height");else if(-1===this._opts.maxEventsPerCell)this._root.classList.remove("uc-unlimited-events"),this._root.style.removeProperty("--cal-cell-min-height");else{this._root.classList.remove("uc-unlimited-events");const t=30+24*this._opts.maxEventsPerCell+28;this._root.style.setProperty("--cal-cell-min-height",`${t}px`)}if("month"===this._view)t.innerHTML=this._buildMonthView();else if("week"===this._view){const e=r(this._date,this._opts.weekStartsOn);t.innerHTML=this._buildWeekOrDayView(e),this._scrollToBusinessHours(t)}else"list"===this._view?t.innerHTML=this._buildListView():(t.innerHTML=this._buildWeekOrDayView([this._date]),this._scrollToBusinessHours(t))}}_scrollToBusinessHours(t){requestAnimationFrame(()=>{const e=t.querySelector(".uc-time-body");if(!e)return;const a=parseFloat(getComputedStyle(this._root).getPropertyValue("--cal-hour-height"))||60;e.scrollTop=7*a})}_renderToolbar(){const t=this._root.querySelector(".uc-toolbar");if(!t)return;const e=document.createElement("div");e.innerHTML=this._buildToolbar(),this._root.replaceChild(e.firstElementChild,t)}_buildShell(){return`\n ${this._buildToolbar()}\n <div class="uc-loading" style="display:none">\n <div class="uc-spinner"></div>\n </div>\n <div class="uc-view-container"></div>\n `}_updateToolbar(){const t=this._root.querySelector(".uc-toolbar");if(t){const e=document.createElement("div");e.innerHTML=this._buildToolbar();const a=e.firstElementChild;a&&t.replaceWith(a)}}_buildToolbar(){if(!this._opts.showToolbar)return"";const t=this._date.getFullYear(),e=(new Date).getFullYear();let a,n=!0;if("month"===this._view)a=new Intl.DateTimeFormat(this._opts.locale,{month:"long"}).format(this._date);else if("week"===this._view){const t=r(this._date,this._opts.weekStartsOn);a=function(t,e,a){if(t.getMonth()===e.getMonth()&&t.getFullYear()===e.getFullYear())return`${new Intl.DateTimeFormat(a,{month:"long"}).format(t)} ${t.getDate()}–${e.getDate()}`;const s=t=>new Intl.DateTimeFormat(a,{month:"short",day:"numeric"}).format(t);return`${s(t)} – ${s(e)}`}(t[0],t[6],this._opts.locale)}else if("list"===this._view){const t=new Date,e=new Date(t);e.setDate(e.getDate()+this._opts.listDaysForward),a=function(t,e,a){if(t.getMonth()===e.getMonth()&&t.getFullYear()===e.getFullYear())return`${new Intl.DateTimeFormat(a,{month:"long"}).format(t)} ${t.getDate()}–${e.getDate()}, ${t.getFullYear()}`;const s=t=>new Intl.DateTimeFormat(a,{month:"short",day:"numeric"}).format(t);return`${s(t)} – ${s(e)}, ${e.getFullYear()}`}(t,e,this._opts.locale),n=!1}else a=new Intl.DateTimeFormat(this._opts.locale,{weekday:"long",month:"long",day:"numeric"}).format(this._date);let i="";if(this._opts.showYearPicker&&this._yearPickerOpen){const a=this._yearPickerBase,s=Array.from({length:12},(s,n)=>{const i=a+n,o=i===t;return`<button class="${"uc-year-item"+(o?" uc-active":"")+(i===e&&!o?" uc-today-year":"")}" data-action="select-year" data-year="${i}">${i}</button>`}).join("");i=`\n <div class="uc-year-picker">\n <div class="uc-year-picker-nav">\n <button class="uc-year-nav-btn" data-action="year-prev" aria-label="Previous years">&#8249;</button>\n <span class="uc-year-range">${a} – ${a+11}</span>\n <button class="uc-year-nav-btn" data-action="year-next" aria-label="Next years">&#8250;</button>\n </div>\n <div class="uc-year-grid">${s}</div>\n </div>`}const o=this._opts.locale,l=_(o,"today"),d=_(o,"month"),c=_(o,"week"),u=_(o,"day"),v=_(o,"list");let g="";if((this._opts.showNavigation||this._opts.showTodayButton)&&"list"!==this._view){const t=this._opts.showNavigation?'<button class="uc-btn uc-nav-btn" data-action="prev" aria-label="Previous">&#8249;</button>':"",e=new Date,a=s(this._date,e)?" uc-active":"";g=`\n <div class="uc-toolbar-section uc-toolbar-left">\n ${t}${this._opts.showTodayButton?`<button class="uc-btn uc-today-btn${a}" data-action="today">${h(l)}</button>`:""}${this._opts.showNavigation?'<button class="uc-btn uc-nav-btn" data-action="next" aria-label="Next">&#8250;</button>':""}\n </div>`}let p="";if(this._opts.showTitle){const e=n?this._opts.showYearPicker?`<button class="uc-year-btn${this._yearPickerOpen?" uc-open":""}" data-action="year-pick" aria-label="Select year">${t}</button>`:t:"";p=`\n <div class="uc-toolbar-section uc-toolbar-center">\n <h2 class="uc-title">\n <span class="uc-title-main">${h(a)}</span>\n ${e}\n </h2>\n ${i}\n </div>`}let y="";if(this._opts.showViewSwitcher){const t=this._opts.enabledViews,e=[];t.includes("month")&&e.push(`<button class="uc-btn uc-view-btn${"month"===this._view?" uc-active":""}" data-view="month">${h(d)}</button>`),t.includes("week")&&e.push(`<button class="uc-btn uc-view-btn${"week"===this._view?" uc-active":""}" data-view="week">${h(c)}</button>`),t.includes("day")&&e.push(`<button class="uc-btn uc-view-btn${"day"===this._view?" uc-active":""}" data-view="day">${h(u)}</button>`),t.includes("list")&&e.push(`<button class="uc-btn uc-view-btn${"list"===this._view?" uc-active":""}" data-view="list">${h(v)}</button>`),e.length>0&&(y=`\n <div class="uc-toolbar-section uc-toolbar-right">\n <div class="uc-view-switcher">\n ${e.join("")}\n </div>\n </div>`)}return`\n <div class="uc-toolbar">\n ${g}${p}${y}\n </div>\n `}_buildMonthView(){const{locale:a,weekStartsOn:s}=this._opts,n=l(this._date.getFullYear(),this._date.getMonth(),s),i=c(a,s,this._opts.weekdayFormat),o=this._events.map(a=>({...a,_origStart:a.start,start:v(a)?t(a.start):a.start,end:v(a)?e(a.end):a.end})),r=i.map(t=>`<div class="uc-month-day-name">${h(t)}</div>`).join(""),d=[];for(let t=0;t<n.length;t+=7)d.push(n.slice(t,t+7));return`\n <div class="uc-month-view">\n <div class="uc-month-header">${r}</div>\n <div class="uc-month-body">${d.map(t=>this._buildWeekRow(t,o)).join("")}</div>\n </div>\n `}_buildWeekRow(t,e){const a=this._date.getMonth();let i;if(0===this._opts.maxEventsPerCell)i=1/0;else if(-1===this._opts.maxEventsPerCell){const t=getComputedStyle(this._root),e=parseInt(t.getPropertyValue("--cal-cell-min-height")||"112");i=Math.floor((e-30-28)/24),i=Math.max(1,i)}else i=this._opts.maxEventsPerCell;const o=e.filter(v),r=e.filter(t=>!v(t)),l=g(o,t),c=Array.from({length:7},()=>new Set);for(const{startCol:t,endCol:e,slot:a}of l)for(let s=t;s<=e;s++)c[s].add(a);const u=t.map(t=>r.filter(e=>s(e.start,t)).sort((t,e)=>t.start-e.start)),_=t.map((t,e)=>`\n <div class="uc-day-cell${n(t)?" uc-today":""}${t.getMonth()!==a?" uc-other-month":""}" data-date="${t.toISOString()}" data-action="day-click">\n <span class="uc-day-number" data-action="day-number" data-date="${t.toISOString()}">${t.getDate()}</span>\n </div>`).join("");let p="";for(const{event:t,startCol:e,endCol:a,slot:s,isStart:n,isEnd:o}of l){if(i!==1/0&&s>=i)continue;const r=100/7,l=e*r,d=(a-e+1)*r,c=`calc(var(--cal-header-day-height) + ${s} * (var(--cal-event-height) + var(--cal-event-gap)) + 4px)`,u=t.color||"var(--cal-event-bg)",_=n?"var(--cal-event-border-radius)":"0",v=o?"var(--cal-event-border-radius)":"0",g=n?"":" uc-continues-left",y=o?"":" uc-continues-right",m=this._opts.showTooltips?`data-tooltip="${h(t.tooltip||t.description||t.title)}"`:"",w=this._opts.enableResize&&o?'<div class="uc-resize-handle-right" data-action="resize-handle"></div>':"";p+=`\n <div class="uc-event-bar${g}${y}"\n style="left:calc(${l}% + 2px);width:calc(${d}% - 4px);top:${c};background:${h(u)};border-radius:${_} ${v} ${v} ${_};"\n data-event-id="${h(t.id)}" data-action="event-click"\n ${m}>\n ${n?`<span class="uc-event-title">${h(t.title)}</span>`:"&nbsp;"}\n ${w}\n </div>`}let y=-1;for(let e=0;e<7;e++){const a=100/7,s=e*a,n=t[e],o=[...c[e]],r=o.length>0?Math.max(...o)+1:0,l=u[e];if(i===1/0)l.forEach((t,e)=>{const n=r+e;y=Math.max(y,n);const i=`calc(var(--cal-header-day-height) + ${n} * (var(--cal-event-height) + var(--cal-event-gap)) + 4px)`,o=t.color||"var(--cal-event-bg)",l=d(t.start,this._opts.locale,this._opts.use24Hour),c=this._opts.showTooltips?`data-tooltip="${h(t.tooltip||t.description||t.title)}"`:"";if("list"===this._opts.monthTimedEventStyle){const e=this._opts.showTimeInItems?`<span class="uc-event-time">${h(l)}</span>`:"";p+=`\n <div class="uc-event-bar uc-event-bar--list"\n style="left:calc(${s}% + 2px);width:calc(${a}% - 4px);top:${i};"\n data-event-id="${h(t.id)}" data-action="event-click"\n ${c}>\n <span class="uc-event-dot" style="background:${h(o)};"></span>\n ${e}\n <span class="uc-event-title">${h(t.title)}</span>\n </div>`}else{const e=this._opts.showTimeInItems?`<span class="uc-event-time">${h(l)}</span>`:"";p+=`\n <div class="uc-event-bar"\n style="left:calc(${s}% + 2px);width:calc(${a}% - 4px);top:${i};background:${h(o)};"\n data-event-id="${h(t.id)}" data-action="event-click"\n ${c}>\n ${e}\n <span class="uc-event-title">${h(t.title)}</span>\n </div>`}});else{const t=[...c[e]].filter(t=>t>=i).length,o=[];for(let t=r;t<i;t++)o.push(t);let u=t;if(l.forEach((t,e)=>{if(e<o.length){const n=o[e];y=Math.max(y,n);const i=`calc(var(--cal-header-day-height) + ${n} * (var(--cal-event-height) + var(--cal-event-gap)) + 4px)`,r=t.color||"var(--cal-event-bg)",l=d(t.start,this._opts.locale,this._opts.use24Hour),c=this._opts.showTooltips?`data-tooltip="${h(t.tooltip||t.description||t.title)}"`:"";if("list"===this._opts.monthTimedEventStyle){const e=this._opts.showTimeInItems?`<span class="uc-event-time">${h(l)}</span>`:"";p+=`\n <div class="uc-event-bar uc-event-bar--list"\n style="left:calc(${s}% + 2px);width:calc(${a}% - 4px);top:${i};"\n data-event-id="${h(t.id)}" data-action="event-click"\n ${c}>\n <span class="uc-event-dot" style="background:${h(r)};"></span>\n ${e}\n <span class="uc-event-title">${h(t.title)}</span>\n </div>`}else{const e=this._opts.showTimeInItems?`<span class="uc-event-time">${h(l)}</span>`:"";p+=`\n <div class="uc-event-bar"\n style="left:calc(${s}% + 2px);width:calc(${a}% - 4px);top:${i};background:${h(r)};"\n data-event-id="${h(t.id)}" data-action="event-click"\n ${c}>\n ${e}\n <span class="uc-event-title">${h(t.title)}</span>\n </div>`}}else u++}),u>0){p+=`\n <div class="uc-more-link"\n style="left:calc(${s}% + 2px);width:calc(${a}% - 4px);top:${`calc(var(--cal-header-day-height) + ${i} * (var(--cal-event-height) + var(--cal-event-gap)) + 2px)`};"\n data-date="${n.toISOString()}" data-action="more-click">\n +${u} more\n </div>`}}}for(const{slot:t}of l)(i===1/0||t<i)&&(y=Math.max(y,t));let m="";return i===1/0&&(m=y>=0?` style="min-height: calc(var(--cal-header-day-height) + ${y+1} * (var(--cal-event-height) + var(--cal-event-gap)) + 8px);"`:' style="min-height: 80px;"'),`\n <div class="uc-week-row"${m}>\n <div class="uc-week-cells">${_}</div>\n <div class="uc-week-events">${p}</div>\n </div>`}_buildWeekOrDayView(a){const{locale:i,weekStartsOn:o,use24Hour:r}=this._opts,l=1===a.length,u=l?" uc-day-header-single":"",p=l?[new Intl.DateTimeFormat(i,{weekday:this._opts.weekdayFormat}).format(a[0])]:c(i,o,this._opts.weekdayFormat),y=t(a[0]),m=e(a[a.length-1]),w=this._events.filter(t=>v(t)&&t.start<=m&&e(t.end)>=y).map(a=>({...a,start:t(a.start),end:e(a.end)})),f=this._events.filter(t=>!v(t)&&t.start>=y&&t.start<=m),D=a.map((t,e)=>{const a=n(t)?" uc-today":"",s=t.getDate();return`\n <div class="uc-week-day-header${a}">\n <span class="uc-week-day-name">${h(p[e])}</span>\n <span class="uc-week-day-num" data-action="day-number" data-date="${t.toISOString()}">${s}</span>\n </div>`}).join(""),S=l?w.map((t,e)=>({event:t,startCol:0,endCol:0,slot:e,isStart:!0,isEnd:!0})):g(w,a),k=S.length?Math.max(...S.map(t=>t.slot))+1:0;let $="";for(const{event:t,startCol:e,endCol:s,slot:n,isStart:i,isEnd:o}of S){const r=100/a.length,l=e*r,d=(s-e+1)*r,c=`calc(${n} * (var(--cal-event-height) + 3px) + 2px)`,u=t.color||"var(--cal-event-bg)",_=i?"var(--cal-event-border-radius)":"0",v=o?"var(--cal-event-border-radius)":"0",g=i?"":" uc-continues-left",p=o?"":" uc-continues-right",y=this._opts.showTooltips?`data-tooltip="${h(t.tooltip||t.description||t.title)}"`:"",m=this._opts.enableResize&&o?'<div class="uc-resize-handle-right" data-action="resize-handle"></div>':"";$+=`\n <div class="uc-event-bar${g}${p}"\n style="left:calc(${l}% + 2px);width:calc(${d}% - 4px);top:${c};background:${h(u)};border-radius:${_} ${v} ${v} ${_};"\n data-event-id="${h(t.id)}" data-action="event-click"\n ${y}>\n <span class="uc-event-title">${h(t.title)}</span>\n ${m}\n </div>`}const b=`calc(${Math.max(1,k)} * (var(--cal-event-height) + 3px) + 6px)`,M=new Date,E=60*M.getHours()+M.getMinutes(),T=a.length,x=Array.from({length:24},(t,e)=>`<div class="uc-hour-cell"><span class="uc-hour-label">${h(0===e?"":d(new Date(2e3,0,1,e),"en-US",r))}</span></div>`).join(""),C=a.map(t=>{const e=n(t)?" uc-today":"",a=Array.from({length:24},()=>'<div class="uc-hour-row"><div class="uc-half-hour-line"></div></div>').join(""),o=function(t){if(!t.length)return[];const e=[...t].sort((t,e)=>t.start-e.start||e.end-t.end),a=[],s=[];for(const t of e){let e=a.findIndex(e=>e<=t.start);-1===e?(e=a.length,a.push(t.end)):a[e]=t.end,s.push({event:t,col:e})}return s.map(t=>{const e=s.filter(e=>e.event.start<t.event.end&&e.event.end>t.event.start),a=Math.max(...e.map(t=>t.col))+1;return{...t,totalCols:a}})}(f.filter(e=>s(e.start,t))).map(({event:t,col:e,totalCols:a})=>{const s=60*t.start.getHours()+t.start.getMinutes(),n=60*t.end.getHours()+t.end.getMinutes(),o=`calc(${s} / 60 * var(--cal-hour-height))`,l=`calc(${Math.max(n-s,30)} / 60 * var(--cal-hour-height))`,c=100/a,u=`calc(${e*c}% + 1px)`,_=`calc(${c}% - 2px)`,v=t.color||"var(--cal-event-bg)",g=d(t.start,i,r),p=n-s<=60?" uc-timed-event--short":"",y=this._opts.showTimeInItems?`<span class="uc-event-time">${h(g)}</span>`:"",m=this._opts.showTooltips?`data-tooltip="${h(t.tooltip||t.description||t.title)}"`:"",w=this._opts.enableResize?'<div class="uc-resize-handle" data-action="resize-handle"></div>':"";return`\n <div class="uc-timed-event${p}"\n style="top:${o};height:${l};left:${u};width:${_};background:${h(v)};"\n data-event-id="${h(t.id)}" data-action="event-click"\n ${m}>\n ${y}\n <span class="uc-event-title">${h(t.title)}</span>\n ${w}\n </div>`}).join(""),l=n(t)?`<div class="uc-now-indicator" style="top:calc(${E} / 60 * var(--cal-hour-height));">\n <span class="uc-now-dot"></span>\n <span class="uc-now-line"></span>\n </div>`:"";return`<div class="uc-time-col${e}" data-date="${t.toISOString()}" data-action="slot-col">\n ${a}${o}${l}\n </div>`}).join("");return`\n <div class="${l?"uc-day-view":"uc-week-view"}">\n <div class="uc-week-header">\n <div class="uc-time-gutter-spacer"></div>\n <div class="uc-week-day-headers${u}">${D}</div>\n </div>\n <div class="uc-all-day-section">\n <div class="uc-time-gutter-spacer uc-all-day-label">${_(this._opts.locale,"allDay")}</div>\n <div class="uc-all-day-events" style="min-height:${b}">${$}</div>\n </div>\n <div class="uc-time-body">\n <div class="uc-time-grid-inner">\n <div class="uc-time-gutter">${x}</div>\n <div class="uc-time-columns" style="--uc-col-count:${T}">${C}</div>\n </div>\n </div>\n </div>`}_buildListView(){const e=new Date,a=t(e),s=new Date(a);s.setDate(s.getDate()+this._opts.listDaysForward);const n=this._events.filter(t=>t.end>=e&&t.start<=s);n.sort((e,s)=>{const n=(e.start<a?a:t(e.start))-(s.start<a?a:t(s.start));return 0!==n?n:e.start-s.start});const i=new Map;if(n.forEach(e=>{const s=e.start<a?a:t(e.start),n=`${(o=s).getFullYear()}-${String(o.getMonth()+1).padStart(2,"0")}-${String(o.getDate()).padStart(2,"0")}`;var o;i.has(n)||i.set(n,[]),i.get(n).push(e)}),0===i.size)return'\n <div class="uc-list-view">\n <div class="uc-list-empty">\n <p>No upcoming events</p>\n </div>\n </div>';let r="";return i.forEach((t,e)=>{const a=o(e),s=new Intl.DateTimeFormat(this._opts.locale,{weekday:"long",year:"numeric",month:"long",day:"numeric"}).format(a),n=t.map(t=>{const e=v(t)?"All day":d(t.start,this._opts.locale,this._opts.use24Hour),a=t.color||"var(--cal-event-bg)",s=this._opts.showTooltips?`data-tooltip="${h(t.tooltip||t.description||"")}"`:"";return`\n <div class="uc-list-event" data-event-id="${h(t.id)}" data-action="event-click" ${s}>\n <div class="uc-list-event-indicator" style="background: ${h(a)};"></div>\n <div class="uc-list-event-time">${h(e)}</div>\n <div class="uc-list-event-content">\n <div class="uc-list-event-title">${h(t.title)}</div>\n </div>\n </div>`}).join("");r+=`\n <div class="uc-list-date-group">\n <div class="uc-list-date-header">${h(s)}</div>\n <div class="uc-list-events">${n}</div>\n </div>`}),`<div class="uc-list-view">${r}</div>`}_closeYearPicker(){this._yearOutsideHandler&&(document.removeEventListener("click",this._yearOutsideHandler),this._yearOutsideHandler=null),this._yearPickerOpen=!1,this._renderToolbar()}_handleClick(e){if(this._clickSuppressed)return void(this._clickSuppressed=!1);const a=e.target.closest("[data-view]");if(a)return void this.setView(a.dataset.view);const s=e.target.closest("[data-action]");if(!s)return;switch(s.dataset.action){case"prev":this.navigate(-1);break;case"next":this.navigate(1);break;case"today":this.goToToday();break;case"year-pick":e.stopPropagation(),this._yearPickerOpen?this._closeYearPicker():(this._yearPickerOpen=!0,this._yearPickerBase=this._date.getFullYear()-4,this._renderToolbar(),this._yearOutsideHandler=t=>{t.target.closest(".uc-year-picker")||t.target.closest('[data-action="year-pick"]')||this._closeYearPicker()},setTimeout(()=>document.addEventListener("click",this._yearOutsideHandler),0));break;case"year-prev":e.stopPropagation(),this._yearPickerBase-=12,this._renderToolbar();break;case"year-next":e.stopPropagation(),this._yearPickerBase+=12,this._renderToolbar();break;case"select-year":{e.stopPropagation();const t=parseInt(s.dataset.year,10);this._date=new Date(this._date.getFullYear()!==t?new Date(this._date).setFullYear(t):this._date),this._closeYearPicker();const a=this._getRange();this._opts.onNavigate&&this._opts.onNavigate(a.start,a.end),this._fetchAndRender();break}case"event-click":{e.stopPropagation();const t=s.dataset.eventId,a=this._events.find(e=>String(e.id)===String(t));a&&this._opts.onEventClick&&this._opts.onEventClick(a,e);break}case"day-click":{if(e.target.closest('[data-action="day-number"]'))break;if(e.target.closest('[data-action="event-click"]'))break;const t=new Date(s.dataset.date);this._opts.onSlotClick&&this._opts.onSlotClick(t,e);break}case"day-number":{if(e.stopPropagation(),!this._opts.enabledViews.includes("day"))break;const a=new Date(s.dataset.date);this._date=t(a),this._view="day",this._opts.onViewChange&&this._opts.onViewChange("day"),this._fetchAndRender();break}case"more-click":{if(e.stopPropagation(),!this._opts.enabledViews.includes("day"))break;const a=new Date(s.dataset.date);this._date=t(a),this._view="day",this._opts.onViewChange&&this._opts.onViewChange("day"),this._fetchAndRender();break}case"slot-col":if(e.target.closest('[data-action="event-click"]'))break;if(this._opts.onSlotClick){const t=s,a=t.getBoundingClientRect(),n=e.clientY-a.top,i=parseFloat(getComputedStyle(this._root).getPropertyValue("--cal-hour-height"))||60,o=Math.max(0,Math.floor(n/i*60)),r=Math.floor(o/60)%24,l=15*Math.round(o%60/15),d=new Date(t.dataset.date);d.setHours(r,l,0,0),this._opts.onSlotClick(d,e)}}}_handleTooltipPosition(t){const e=t.target.closest("[data-tooltip]");if(!e||!e.dataset.tooltip)return;if(!e.dataset.tooltip.trim())return;const a=e.getBoundingClientRect(),s=window.innerWidth;e.classList.remove("uc-tooltip-left","uc-tooltip-right","uc-tooltip-bottom");const n=a.width/2-125;a.left+n+250>s-10?e.classList.add("uc-tooltip-left"):a.left+n<10&&e.classList.add("uc-tooltip-right"),a.top<50&&e.classList.add("uc-tooltip-bottom")}_handleDragStart(t){if("list"===this._view)return;const e=t.target.closest('[data-action="resize-handle"]'),a=!!e,s=e&&e.classList.contains("uc-resize-handle-right"),n=e&&e.classList.contains("uc-resize-handle"),i=t.target.closest('[data-event-id][data-action="event-click"]');if(!i)return;if(t.target.closest('.uc-year-picker, [data-action="year-pick"], .uc-toolbar'))return;const o=i.dataset.eventId,r=this._events.find(t=>String(t.id)===String(o));if(!r)return;const l=r.allDay||!1;if(a&&!this._opts.enableResize)return;if(!a&&!this._opts.enableDragDrop)return;if(n&&(l||"week"!==this._view&&"day"!==this._view))return;if(s&&!l)return;const d=this._getEventPosition(t),c=i.getBoundingClientRect(),h=d.x-c.left,u=d.y-c.top;this._dragState={mode:a?"resize":"move",resizeDirection:s?"horizontal":n?"vertical":null,eventId:o,originalEvent:{...r,start:new Date(r.start),end:new Date(r.end)},dragElement:null,startX:d.x,startY:d.y,currentX:d.x,currentY:d.y,offsetX:h,offsetY:u,originalView:this._view,isAllDay:l,hasMoved:!1,startTime:Date.now(),originalElement:i}}_handleDragMove(t){if(!this._dragState)return;const e=this._getEventPosition(t);this._dragState.currentX=e.x,this._dragState.currentY=e.y;const a=e.x-this._dragState.startX,s=e.y-this._dragState.startY,n=Math.sqrt(a*a+s*s);if(!this._dragState.hasMoved){const t=Date.now()-this._dragState.startTime;if(!(n>5||t>150))return;this._dragState.hasMoved=!0,this._createDragElement(),this._root.classList.add("uc-dragging")}if(t.type.startsWith("touch")&&t.preventDefault(),this._dragState.dragElement)if("resize"===this._dragState.mode&&"vertical"===this._dragState.resizeDirection)this._updateResizePreview(e.x,e.y);else if("resize"===this._dragState.mode&&"horizontal"===this._dragState.resizeDirection)this._updateHorizontalResizePreview(e.x);else{const t=this._getSnappedPosition(e.x,e.y);t?(this._dragState.dragElement.style.left=t.x+"px",this._dragState.dragElement.style.top=t.y+"px"):(this._dragState.dragElement.style.left=e.x-this._dragState.offsetX+"px",this._dragState.dragElement.style.top=e.y-this._dragState.offsetY+"px")}}_getSnappedPosition(t,e){if("week"!==this._view&&"day"!==this._view)return null;const a=this._calculateDropPosition(t,e);if(!a)return null;const s=this._dragState.dragElement;if(!s)return null;if(!a.isAllDay){const e=this._root.querySelector(".uc-time-columns");if(!e)return null;const n=e.querySelectorAll(".uc-time-col");let i=null;for(const e of n){const a=e.getBoundingClientRect();if(t>=a.left&&t<a.right){i=e;break}}if(!i)return null;const o=i.getBoundingClientRect(),r=(60*a.date.getHours()+a.date.getMinutes())/60*(parseFloat(getComputedStyle(this._root).getPropertyValue("--cal-hour-height"))||60),l=s.querySelector(".uc-event-time");if(l){const t=d(a.date,this._opts.locale,this._opts.use24Hour);l.textContent=t}return{x:o.left+4,y:o.top+r}}if(a.isAllDay){const e=this._root.querySelector(".uc-all-day-events");if(!e)return null;const a=e.getBoundingClientRect(),s="day"===this._view?[this._date]:r(this._date,this._opts.weekStartsOn),n=a.width/s.length,i=t-a.left,o=Math.floor(i/n);return o<0||o>=s.length?null:{x:a.left+o*n+4,y:a.top+4}}return null}_updateResizePreview(t,e){const a=this._dragState.dragElement,s=this._dragState.originalEvent,n=this._root.querySelector(".uc-time-columns");if(!n)return;const i=n.querySelectorAll(".uc-time-col");let o=null;for(const e of i){const a=e.getBoundingClientRect();if(t>=a.left&&t<a.right){o=e;break}}if(!o)return;const r=o.getBoundingClientRect(),l=Math.max(0,e-r.top),c=parseFloat(getComputedStyle(this._root).getPropertyValue("--cal-hour-height"))||60,h=Math.floor(l/c*60),u=Math.floor(h/60)%24,_=15*Math.round(h%60/15),v=new Date(s.start);v.setHours(u,_,0,0),v<=s.start&&v.setTime(s.start.getTime()+9e5);const g=(v-s.start)/6e4/60*c;a.style.height=g+"px";const p=a.querySelector(".uc-event-time");if(p){const t=d(s.start,this._opts.locale,this._opts.use24Hour),e=d(v,this._opts.locale,this._opts.use24Hour);p.textContent=`${t} - ${e}`}this._dragState.newEndTime=v}_updateHorizontalResizePreview(t){const a=this._dragState.dragElement,s=this._dragState.originalEvent,n=this._dragState.originalElement;if(!n)return;const i=n.getBoundingClientRect(),o=t-i.left;let d;if("month"===this._view){const a=this._root.querySelector(".uc-month-body");if(!a)return;const s=a.getBoundingClientRect(),n=s.width/7,o=t-s.left,r=Math.floor(o/n),c=l(this._date.getFullYear(),this._date.getMonth(),this._opts.weekStartsOn),h=i.top-s.top,u=s.height/Math.ceil(c.length/7),_=7*Math.floor(h/u)+r;_>=0&&_<c.length&&(d=e(c[_]))}else{const a="day"===this._view?[this._date]:r(this._date,this._opts.weekStartsOn),s=this._root.querySelector(".uc-all-day-events");if(!s)return;const n=s.getBoundingClientRect(),i=n.width/a.length,o=t-n.left,l=Math.floor(o/i);l>=0&&l<a.length&&(d=e(a[l]))}if(!d)return;d<s.start&&(d=e(s.start));const c=o+20;a.style.width=Math.max(c,60)+"px",this._dragState.newEndDate=d}_handleDragEnd(t){if(!this._dragState)return;if(this._dragState.hasMoved){if(t.preventDefault(),t.stopPropagation(),this._clickSuppressed=!0,setTimeout(()=>{this._clickSuppressed=!1},10),"resize"===this._dragState.mode&&"vertical"===this._dragState.resizeDirection)this._dragState.newEndTime&&this._performResize(this._dragState.newEndTime);else if("resize"===this._dragState.mode&&"horizontal"===this._dragState.resizeDirection)this._dragState.newEndDate&&this._performHorizontalResize(this._dragState.newEndDate);else{const e=this._getEventPosition(t),a=this._calculateDropPosition(e.x,e.y);a&&this._performDrop(a)}this._cleanupDrag()}else this._dragState=null}_handleDragKeyDown(t){this._dragState&&this._dragState.hasMoved&&"Escape"===t.key&&this._cleanupDrag()}_getEventPosition(t){if(t.type.startsWith("touch")){const e=t.touches[0]||t.changedTouches[0];return{x:e.clientX,y:e.clientY}}return{x:t.clientX,y:t.clientY}}_createDragElement(){const t=this._root.querySelector(`[data-event-id="${this._dragState.eventId}"]`);if(!t)return;const e=t.cloneNode(!0);e.classList.add("uc-dragging-element");const a=t.getBoundingClientRect();e.style.position="fixed",e.style.pointerEvents="none",e.style.zIndex="10000",e.style.opacity="0.8",e.style.width=t.offsetWidth+"px","resize"===this._dragState.mode?(e.style.left=a.left+"px",e.style.top=a.top+"px",e.style.height=t.offsetHeight+"px"):(e.style.left=this._dragState.currentX-this._dragState.offsetX+"px",e.style.top=this._dragState.currentY-this._dragState.offsetY+"px"),document.body.appendChild(e),this._dragState.dragElement=e}_cleanupDrag(){this._dragState&&this._dragState.dragElement&&this._dragState.dragElement.remove(),this._root.classList.remove("uc-dragging"),this._dragState=null}_calculateDropPosition(t,e){if("week"===this._view||"day"===this._view){const a=this._calculateAllDayDropPosition(t,e);if(a)return a;const s=this._calculateTimedDropPosition(t,e);if(s)return s}return"month"===this._view?this._calculateMonthDropPosition(t,e):null}_calculateMonthDropPosition(t,e){const a=this._root.querySelector(".uc-month-body");if(!a)return null;const s=a.getBoundingClientRect(),n=t-s.left,i=e-s.top,o=s.width/7,r=a.querySelectorAll(".uc-week-row").length,d=s.height/r,c=Math.floor(n/o),h=Math.floor(i/d),u=l(this._date.getFullYear(),this._date.getMonth(),this._opts.weekStartsOn),_=7*h+c;return c<0||c>=7||_<0||_>=u.length?null:{date:u[_],isMonthView:!0}}_calculateTimedDropPosition(t,e){const a=this._root.querySelector(".uc-time-columns");if(!a)return null;const s=a.querySelectorAll(".uc-time-col");let n=null;for(const e of s){const a=e.getBoundingClientRect();if(t>=a.left&&t<a.right){n=e;break}}if(!n)return null;const i=n.getBoundingClientRect(),o=Math.max(0,e-i.top),r=parseFloat(getComputedStyle(this._root).getPropertyValue("--cal-hour-height"))||60,l=Math.floor(o/r*60),d=Math.floor(l/60)%24,c=15*Math.round(l%60/15),h=n.dataset.date;if(!h)return null;const u=new Date(h);return u.setHours(d,c,0,0),{date:u,isAllDay:!1}}_calculateAllDayDropPosition(e,a){const s=this._root.querySelector(".uc-all-day-events");if(!s)return null;const n=s.getBoundingClientRect();if(a<n.top||a>n.bottom)return null;const i="day"===this._view?[this._date]:r(this._date,this._opts.weekStartsOn),o=n.width/i.length,l=e-n.left,d=Math.floor(l/o);return d<0||d>=i.length?null:{date:t(i[d]),isAllDay:!0}}_performDrop(s){const n=this._dragState.originalEvent,o=new Date(n.start),r=new Date(n.end);let l,d,c;const h=n.allDay||this._dragState.isAllDay;if(s.isMonthView)if(h){const n=i(o,r);l=t(s.date),d=e(a(s.date,Math.max(0,n))),c=!0}else{const t=r-o;l=new Date(s.date),l.setHours(o.getHours(),o.getMinutes(),o.getSeconds(),o.getMilliseconds()),d=new Date(l.getTime()+t),c=!1}else if(s.isAllDay){const n=Math.max(0,Math.ceil((r-o)/864e5));l=t(s.date),d=e(a(s.date,n)),c=!0}else{if(l=s.date,h)d=new Date(l.getTime()+36e5);else{const t=r-o;d=new Date(l.getTime()+t)}c=!1}const u=this._events.findIndex(t=>String(t.id)===String(this._dragState.eventId));u>=0&&(this._events[u].start=l,this._events[u].end=d,this._events[u].allDay=c);const _=this._cachedEvents.findIndex(t=>String(t.id)===String(this._dragState.eventId));_>=0&&(this._cachedEvents[_].start=l,this._cachedEvents[_].end=d,this._cachedEvents[_].allDay=c),this._opts.onEventDrop&&this._opts.onEventDrop(u>=0?this._events[u]:null,o,r,l,d),this._renderView()}_performResize(t){const e=this._dragState.originalEvent,a=new Date(e.start),s=new Date(e.end),n=a,i=this._events.findIndex(t=>String(t.id)===String(this._dragState.eventId));i>=0&&(this._events[i].end=t);const o=this._cachedEvents.findIndex(t=>String(t.id)===String(this._dragState.eventId));o>=0&&(this._cachedEvents[o].end=t),this._opts.onEventDrop&&this._opts.onEventDrop(i>=0?this._events[i]:null,a,s,n,t),this._renderView()}_performHorizontalResize(t){const e=this._dragState.originalEvent,a=new Date(e.start),s=new Date(e.end),n=a,i=this._events.findIndex(t=>String(t.id)===String(this._dragState.eventId));i>=0&&(this._events[i].end=t);const o=this._cachedEvents.findIndex(t=>String(t.id)===String(this._dragState.eventId));o>=0&&(this._cachedEvents[o].end=t),this._opts.onEventDrop&&this._opts.onEventDrop(i>=0?this._events[i]:null,a,s,n,t),this._renderView()}_startNowUpdater(){this._nowInterval=setInterval(()=>{const t=this._root.querySelectorAll(".uc-now-indicator");if(!t.length)return;const e=new Date,a=`calc(${60*e.getHours()+e.getMinutes()} / 60 * var(--cal-hour-height))`;t.forEach(t=>t.style.top=a)},6e4)}}});
@@ -1,5 +1,5 @@
1
1
  /**
2
- * SimpleCalendarJs v3.0.4 — Angular Wrapper
2
+ * SimpleCalendarJs v3.0.5 — Angular Wrapper
3
3
  * A clean, modern, and feature-rich JavaScript calendar component with zero dependencies
4
4
  *
5
5
  * @author Pedro Lopes <simplecalendarjs@gmail.com>
@@ -1,5 +1,5 @@
1
1
  /**
2
- * SimpleCalendarJs v3.0.4 — React Wrapper
2
+ * SimpleCalendarJs v3.0.5 — React Wrapper
3
3
  * A clean, modern, and feature-rich JavaScript calendar component with zero dependencies
4
4
  *
5
5
  * @author Pedro Lopes <simplecalendarjs@gmail.com>
@@ -1,5 +1,5 @@
1
1
  /**
2
- * SimpleCalendarJs v3.0.4 — Vue 3 Wrapper
2
+ * SimpleCalendarJs v3.0.5 — Vue 3 Wrapper
3
3
  * A clean, modern, and feature-rich JavaScript calendar component with zero dependencies
4
4
  *
5
5
  * @author Pedro Lopes <simplecalendarjs@gmail.com>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "simple-calendar-js",
3
- "version": "3.0.4",
3
+ "version": "3.0.6",
4
4
  "description": "A clean, modern, and feature-rich JavaScript calendar component with zero dependencies. Responsive design and intuitive navigation.",
5
5
  "main": "dist/simple-calendar-js.min.js",
6
6
  "style": "dist/simple-calendar-js.min.css",
@@ -8,7 +8,8 @@
8
8
  "dist/",
9
9
  "frameworks/",
10
10
  "LICENSE",
11
- "README.md"
11
+ "README.md",
12
+ "CHANGELOG.md"
12
13
  ],
13
14
  "scripts": {
14
15
  "test": "echo \"Error: no test specified\" && exit 1",