simple-calendar-js 3.0.0 → 3.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -2,7 +2,7 @@
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.0-blue.svg)
5
+ ![Version](https://img.shields.io/badge/version-3.0.2-blue.svg)
6
6
  ![License](https://img.shields.io/badge/license-See%20LICENSE-green.svg)
7
7
 
8
8
  ## Features
@@ -163,17 +163,19 @@ export class CalendarComponent {
163
163
  | `defaultDate` | Date | `new Date()` | Initial date to display |
164
164
  | `weekStartsOn` | number | `0` | First day of week: `0` (Sunday) or `1` (Monday) |
165
165
  | `locale` | string | `'default'` | Locale code for Intl API (e.g., `'en-US'`, `'fr-FR'`, `'ja-JP'`) |
166
+ | `weekdayFormat` | string | `'short'` | Weekday name format: `'narrow'` (1-2 letters), `'short'` (abbreviated), or `'long'` (full name) |
166
167
  | `use24Hour` | boolean | `false` | Use 24-hour time format |
167
168
  | `showTimeInItems` | boolean | `true` | Show time in event items |
168
169
  | `showGridLines` | boolean | `true` | Show calendar grid lines |
170
+ | `showBorder` | boolean | `true` | Show calendar outer border |
169
171
  | `showToolbar` | boolean | `true` | Show the toolbar |
170
172
  | `showTodayButton` | boolean | `true` | Show "Today" button |
171
173
  | `showNavigation` | boolean | `true` | Show prev/next navigation arrows |
172
174
  | `showTitle` | boolean | `true` | Show month/year title |
173
175
  | `showYearPicker` | boolean | `true` | Enable year picker dropdown (month view) |
174
176
  | `showViewSwitcher` | boolean | `true` | Show view switcher buttons |
177
+ | `showTooltips` | boolean | `true` | Show tooltips on hover for events |
175
178
  | `enabledViews` | string[] | `['month', 'week', 'day']` | Available view modes |
176
- | `primaryColor` | string | `'#4f46e5'` | Primary theme color (hex) |
177
179
  | `fetchEvents` | function | `null` | Async function to fetch events: `async (start, end) => Event[]` |
178
180
  | `onEventClick` | function | `null` | Callback when event is clicked: `(event, mouseEvent) => void` |
179
181
  | `onSlotClick` | function | `null` | Callback when time slot is clicked: `(date, mouseEvent) => void` |
@@ -192,6 +194,8 @@ interface CalendarEvent {
192
194
  end?: Date; // End date/time (optional, defaults to start)
193
195
  allDay?: boolean; // All-day event flag (optional)
194
196
  color?: string; // Custom color (hex, optional)
197
+ description?: string; // Event description (also used for tooltip if tooltip not provided)
198
+ tooltip?: string; // Custom tooltip text shown on hover (overrides description)
195
199
  [key: string]: any; // Any additional custom properties
196
200
  }
197
201
  ```
@@ -205,7 +209,8 @@ const events = [
205
209
  title: 'Team Meeting',
206
210
  start: new Date('2024-03-15T10:00:00'),
207
211
  end: new Date('2024-03-15T11:00:00'),
208
- color: '#3b82f6'
212
+ color: '#3b82f6',
213
+ tooltip: 'Weekly team sync\nDiscuss Q1 roadmap and sprint planning'
209
214
  },
210
215
  {
211
216
  id: 2,
@@ -213,11 +218,115 @@ const events = [
213
218
  start: new Date('2024-03-20T00:00:00'),
214
219
  end: new Date('2024-03-22T23:59:59'),
215
220
  allDay: true,
216
- color: '#10b981'
221
+ color: '#10b981',
222
+ description: 'Tech Conference 2024\nDowntown Convention Center'
217
223
  }
218
224
  ];
219
225
  ```
220
226
 
227
+ ## Tooltips
228
+
229
+ SimpleCalendarJs includes built-in tooltip support for displaying additional event information on hover.
230
+
231
+ ### How Tooltips Work
232
+
233
+ Tooltips appear when you hover over an event for 400ms. They display content based on the following priority:
234
+
235
+ 1. **`tooltip` property** (highest priority) - Custom tooltip text
236
+ 2. **`description` property** - Falls back if no tooltip is provided
237
+ 3. **`title` property** - Falls back if neither tooltip nor description is provided
238
+
239
+ ### Basic Usage
240
+
241
+ ```javascript
242
+ const events = [
243
+ {
244
+ id: 1,
245
+ title: 'Team Meeting',
246
+ start: new Date('2024-03-15T10:00:00'),
247
+ end: new Date('2024-03-15T11:00:00'),
248
+ tooltip: 'Weekly team sync\nDiscuss Q1 roadmap' // Custom tooltip
249
+ },
250
+ {
251
+ id: 2,
252
+ title: 'Client Call',
253
+ start: new Date('2024-03-15T14:00:00'),
254
+ end: new Date('2024-03-15T15:00:00'),
255
+ description: 'Requirements gathering\nPrepare demo' // Used as tooltip
256
+ },
257
+ {
258
+ id: 3,
259
+ title: 'Code Review',
260
+ start: new Date('2024-03-15T16:00:00'),
261
+ end: new Date('2024-03-15T17:00:00')
262
+ // No tooltip/description - will show title
263
+ }
264
+ ];
265
+ ```
266
+
267
+ ### Multiline Tooltips
268
+
269
+ Use `\n` (newline character) in your tooltip or description text to create multiple lines:
270
+
271
+ ```javascript
272
+ {
273
+ id: 1,
274
+ title: 'Project Kickoff',
275
+ start: new Date('2024-03-20T09:00:00'),
276
+ end: new Date('2024-03-20T10:30:00'),
277
+ tooltip: 'Project Kickoff Meeting\n\nAgenda:\n- Introductions\n- Timeline review\n- Q&A session\n\nLocation: Conference Room A'
278
+ }
279
+ ```
280
+
281
+ ### Tooltip Features
282
+
283
+ - **Smart Positioning**: Tooltips automatically adjust their position to stay visible:
284
+ - Events near the **top** of the viewport: tooltip shows below
285
+ - Events near the **right edge**: tooltip aligns to the right
286
+ - Events near the **left edge**: tooltip aligns to the left
287
+
288
+ - **Visual Design**:
289
+ - Dark background with rounded corners
290
+ - Small arrow pointing to the event
291
+ - Smooth fade-in animation (400ms delay)
292
+ - Supports both light and dark themes
293
+
294
+ - **Responsive**: Tooltips overflow outside calendar boundaries to ensure full visibility
295
+
296
+ ### Disabling Tooltips
297
+
298
+ You can disable tooltips globally using the `showTooltips` option:
299
+
300
+ ```javascript
301
+ const calendar = new SimpleCalendarJs('#calendar', {
302
+ showTooltips: false, // Disable all tooltips
303
+ fetchEvents: async (start, end) => { ... }
304
+ });
305
+ ```
306
+
307
+ ### Styling Tooltips
308
+
309
+ Tooltips use CSS custom properties and can be customized:
310
+
311
+ ```css
312
+ :root {
313
+ --cal-tooltip-bg: #1f2937; /* Background color */
314
+ --cal-tooltip-text: #f9fafb; /* Text color */
315
+ --cal-tooltip-border: #374151; /* Border color */
316
+ --cal-tooltip-max-width: 250px; /* Maximum width */
317
+ --cal-tooltip-padding: 8px 12px; /* Inner padding */
318
+ --cal-tooltip-radius: 6px; /* Border radius */
319
+ --cal-tooltip-font-size: 12px; /* Font size */
320
+ --cal-tooltip-offset: 8px; /* Distance from event */
321
+ }
322
+ ```
323
+
324
+ ### Limitations
325
+
326
+ - Tooltips display **plain text only** (no HTML rendering)
327
+ - Special characters are automatically escaped for security
328
+ - Maximum width is 250px by default (can be customized via CSS variables)
329
+
221
330
  ## API Methods
222
331
 
223
332
  ```javascript
@@ -262,6 +371,46 @@ const calendar = new SimpleCalendarJs('#calendar', {
262
371
 
263
372
  Supported locales include: `en-US`, `en-GB`, `fr-FR`, `de-DE`, `es-ES`, `it-IT`, `pt-PT`, `pt-BR`, `ja-JP`, `zh-CN`, `ko-KR`, `ar-SA`, `ru-RU`, `tr-TR`, and [many more](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/DateTimeFormat/DateTimeFormat#locales).
264
373
 
374
+ ### Weekday Name Format
375
+
376
+ Control how day names are displayed using the `weekdayFormat` option:
377
+
378
+ ```javascript
379
+ const calendar = new SimpleCalendarJs('#calendar', {
380
+ locale: 'pt-PT',
381
+ weekdayFormat: 'long' // Full day names
382
+ });
383
+ ```
384
+
385
+ **Available formats:**
386
+ - `'narrow'` - 1-2 letter abbreviation (S, M, T, W, T, F, S)
387
+ - `'short'` - Short abbreviation (Sun, Mon, Tue... or Dom, Seg, Ter...)
388
+ - `'long'` - Full day name (Sunday, Monday... or Domingo, Segunda-feira...)
389
+
390
+ **Examples:**
391
+
392
+ ```javascript
393
+ // English with narrow format
394
+ new SimpleCalendarJs('#calendar', {
395
+ locale: 'en-US',
396
+ weekdayFormat: 'narrow' // S M T W T F S
397
+ });
398
+
399
+ // Portuguese (Portugal) with long format
400
+ new SimpleCalendarJs('#calendar', {
401
+ locale: 'pt-PT',
402
+ weekdayFormat: 'long' // Domingo Segunda-feira Terça-feira...
403
+ });
404
+
405
+ // Portuguese (Brazil) with short format (default)
406
+ new SimpleCalendarJs('#calendar', {
407
+ locale: 'pt-BR',
408
+ weekdayFormat: 'short' // Dom Seg Ter Qua...
409
+ });
410
+ ```
411
+
412
+ **Note:** Different locales may format the same option differently. For example, `'short'` in `pt-PT` might display "Sábado" while `pt-BR` displays "Sáb." - this is controlled by the browser's Intl API based on locale conventions. Use `weekdayFormat` to control the length/style consistently.
413
+
265
414
  ## Dark Mode
266
415
 
267
416
  Dark mode is automatically detected from the user's system preferences. You can also manually toggle it:
@@ -289,40 +438,88 @@ For framework wrappers, use the `darkMode` prop:
289
438
 
290
439
  ## Styling & Theming
291
440
 
292
- Customize the calendar appearance using CSS custom properties:
441
+ Customize the calendar appearance using CSS custom properties.
442
+
443
+ ### Override Light Mode Colors
444
+
445
+ Target the `.uc-calendar` class to customize light mode:
293
446
 
294
447
  ```css
295
448
  .uc-calendar {
296
449
  /* Primary color */
297
- --cal-primary: #4f46e5;
298
- --cal-primary-dark: #4338ca;
299
- --cal-primary-light: #eef2ff;
450
+ --cal-primary: #10b981; /* Green theme */
451
+ --cal-primary-dark: #059669;
452
+ --cal-primary-light: #d1fae5;
453
+
454
+ /* Today indicator */
455
+ --cal-today-bg: #d1fae5;
456
+ --cal-today-text: #059669;
457
+ }
458
+ ```
300
459
 
301
- /* Background colors */
302
- --cal-bg: #ffffff;
303
- --cal-bg-secondary: #f9fafb;
460
+ ### Override Dark Mode Colors
304
461
 
305
- /* Text colors */
306
- --cal-text: #111827;
307
- --cal-text-subtle: #6b7280;
462
+ Target `.uc-calendar.uc-dark` to customize dark mode:
308
463
 
309
- /* Borders */
310
- --cal-border: #e5e7eb;
464
+ ```css
465
+ .uc-calendar.uc-dark {
466
+ /* Dark mode colors */
467
+ --cal-bg: #1f2937;
468
+ --cal-bg-secondary: #111827;
469
+ --cal-text: #f9fafb;
470
+ --cal-border: #374151;
471
+
472
+ /* Dark mode today/selected indicators */
473
+ --cal-today-bg: #065f46; /* Custom dark green */
474
+ --cal-selected-bg: #047857;
475
+ }
476
+ ```
311
477
 
312
- /* Today indicator */
313
- --cal-today-bg: #eef2ff;
314
- --cal-today-text: var(--cal-primary);
478
+ ### Customize Both Light and Dark Modes
315
479
 
316
- /* Events */
317
- --cal-event-bg: var(--cal-primary);
318
- --cal-event-text: #ffffff;
480
+ Example with a complete brand color scheme:
319
481
 
320
- /* Sizing */
321
- --cal-font-size: 13px;
322
- --cal-hour-height: 60px;
482
+ ```css
483
+ /* Light mode - Yellow theme */
484
+ .uc-calendar {
485
+ --cal-primary: #eab308; /* yellow-500 */
486
+ --cal-primary-dark: #ca8a04; /* yellow-600 */
487
+ --cal-primary-light: #fef9c3; /* yellow-100 */
488
+ --cal-today-bg: #fef9c3;
489
+ --cal-today-text: #854d0e;
490
+ }
491
+
492
+ /* Dark mode - Yellow theme */
493
+ .uc-calendar.uc-dark {
494
+ --cal-primary: #eab308;
495
+ --cal-primary-dark: #facc15;
496
+ --cal-primary-light: #713f12;
497
+ --cal-today-bg: #713f12; /* yellow-900 */
498
+ --cal-selected-bg: #854d0e; /* yellow-800 */
323
499
  }
324
500
  ```
325
501
 
502
+ ### Available CSS Variables
503
+
504
+ **Colors:**
505
+ - `--cal-primary`, `--cal-primary-dark`, `--cal-primary-light` - Primary theme colors
506
+ - `--cal-bg`, `--cal-bg-secondary` - Background colors
507
+ - `--cal-text`, `--cal-text-subtle`, `--cal-text-muted` - Text colors
508
+ - `--cal-border`, `--cal-border-strong` - Border colors
509
+ - `--cal-today-bg`, `--cal-today-text` - Today indicator
510
+ - `--cal-selected-bg` - Selected date background
511
+ - `--cal-hover`, `--cal-hover-strong` - Hover states
512
+
513
+ **Sizing:**
514
+ - `--cal-font-size` - Base font size (default: 13px)
515
+ - `--cal-hour-height` - Hour row height in week/day views (default: 60px)
516
+ - `--cal-event-height` - Event bar height (default: 22px)
517
+
518
+ **Tooltips:**
519
+ - `--cal-tooltip-bg`, `--cal-tooltip-text`, `--cal-tooltip-border` - Tooltip colors
520
+ - `--cal-tooltip-max-width`, `--cal-tooltip-padding`, `--cal-tooltip-radius` - Tooltip sizing
521
+ - `--cal-tooltip-font-size`, `--cal-tooltip-offset` - Tooltip typography
522
+
326
523
  All styles are scoped under `.uc-calendar` to prevent conflicts with your existing CSS.
327
524
 
328
525
  ## Framework Wrapper APIs
@@ -1,13 +1,12 @@
1
1
  /**
2
- * SimpleCalendarJs v3.0.0 — simple-calendar-js.css
2
+ * SimpleCalendarJs v3.0.2 — 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>
6
6
  * @homepage https://www.simplecalendarjs.com
7
7
  * @license SEE LICENSE IN LICENSE
8
- * @repository https://github.com/pclslopes/SimpleCalendarJs
9
8
  *
10
9
  * All styles scoped under .uc-calendar to prevent leaking.
11
10
  * Override any --cal-* variable in :root or on .uc-calendar.
12
11
  */
13
- :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}.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;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);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:hidden;display:flex;flex-direction:column}.uc-month-view{display:flex;flex-direction:column;flex:1;overflow:hidden}.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:hidden}.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}.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:hidden}.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;overflow:hidden;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)}.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:var(--cal-event-height);display:flex;align-items:center;padding:0 6px;font-size:11px;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:hidden}.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:hidden;position:relative}.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;overflow:hidden;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:4}.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-time-col:hover{background:var(--cal-hover)}.uc-time-col.uc-today:hover{background:color-mix(in srgb,var(--cal-today-bg) 80%,var(--cal-hover-strong))}@supports not (color:color-mix(in srgb,red 50%,blue)){.uc-time-col.uc-today:hover{background:var(--cal-today-bg)}}@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-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:#312e81;--cal-today-bg:#1e1b4b;--cal-primary-light:#1e1b4b;--cal-toolbar-bg:#111827}@media print{.uc-toolbar{display:none}.uc-time-body{overflow:visible}.uc-calendar{border:none}}
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);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-month-view{display:flex;flex-direction:column;flex:1;overflow:visible}.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}.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}.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}.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-time-col:hover{background:var(--cal-hover)}.uc-time-col.uc-today:hover{background:color-mix(in srgb,var(--cal-today-bg) 80%,var(--cal-hover-strong))}@supports not (color:color-mix(in srgb,red 50%,blue)){.uc-time-col.uc-today:hover{background:var(--cal-today-bg)}}.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}}
@@ -1,15 +1,14 @@
1
1
  /**
2
- * SimpleCalendarJs v3.0.0
2
+ * SimpleCalendarJs v3.0.2
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
- * @repository https://github.com/pclslopes/SimpleCalendarJs
9
8
  */
10
- !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 n(t,e){return t.getFullYear()===e.getFullYear()&&t.getMonth()===e.getMonth()&&t.getDate()===e.getDate()}function s(t){return n(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,n){const s=e.getDay(),i=t(a(e,-((s-n+7)%7)));return Array.from({length:7},(t,e)=>a(i,e))}function c(t,e,n){const s=new Date(t,e,1),i=new Date(t,e+1,0),o=(s.getDay()-n+7)%7,r=7*Math.ceil((o+i.getDate())/7),c=a(s,-o);return Array.from({length:r},(t,e)=>a(c,e))}function l(t,e,a){const n={hour:"numeric",hour12:!a};return 0!==t.getMinutes()&&(n.minute="2-digit"),new Intl.DateTimeFormat(e,n).format(t)}function d(t,e,a){return Array.from({length:7},(n,s)=>{const i=new Date(2025,0,5+(e+s)%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"},"en-GB":{today:"Today",month:"Month",week:"Week",day:"Day"},"es-ES":{today:"Hoy",month:"Mes",week:"Semana",day:"Día"},"es-MX":{today:"Hoy",month:"Mes",week:"Semana",day:"Día"},"fr-FR":{today:"Aujourd'hui",month:"Mois",week:"Semaine",day:"Jour"},"fr-CA":{today:"Aujourd'hui",month:"Mois",week:"Semaine",day:"Jour"},"de-DE":{today:"Heute",month:"Monat",week:"Woche",day:"Tag"},"it-IT":{today:"Oggi",month:"Mese",week:"Settimana",day:"Giorno"},"pt-PT":{today:"Hoje",month:"Mês",week:"Semana",day:"Dia"},"pt-BR":{today:"Hoje",month:"Mês",week:"Semana",day:"Dia"},"nl-NL":{today:"Vandaag",month:"Maand",week:"Week",day:"Dag"},"pl-PL":{today:"Dzisiaj",month:"Miesiąc",week:"Tydzień",day:"Dzień"},"ru-RU":{today:"Сегодня",month:"Месяц",week:"Неделя",day:"День"},"tr-TR":{today:"Bugün",month:"Ay",week:"Hafta",day:"Gün"},"sv-SE":{today:"Idag",month:"Månad",week:"Vecka",day:"Dag"},"da-DK":{today:"I dag",month:"Måned",week:"Uge",day:"Dag"},"fi-FI":{today:"Tänään",month:"Kuukausi",week:"Viikko",day:"Päivä"},"no-NO":{today:"I dag",month:"Måned",week:"Uke",day:"Dag"},"cs-CZ":{today:"Dnes",month:"Měsíc",week:"Týden",day:"Den"},"hu-HU":{today:"Ma",month:"Hónap",week:"Hét",day:"Nap"},"ro-RO":{today:"Astăzi",month:"Lună",week:"Săptămână",day:"Zi"},"el-GR":{today:"Σήμερα",month:"Μήνας",week:"Εβδομάδα",day:"Ημέρα"},"ja-JP":{today:"今日",month:"月",week:"週",day:"日"},"ko-KR":{today:"오늘",month:"월",week:"주",day:"일"},"zh-CN":{today:"今天",month:"月",week:"周",day:"日"},"zh-TW":{today:"今天",month:"月",week:"週",day:"日"},"ar-SA":{today:"اليوم",month:"شهر",week:"أسبوع",day:"يوم"},"he-IL":{today:"היום",month:"חודש",week:"שבוע",day:"יום"},"hi-IN":{today:"आज",month:"महीना",week:"सप्ताह",day:"दिन"},"th-TH":{today:"วันนี้",month:"เดือน",week:"สัปดาห์",day:"วัน"},"vi-VN":{today:"Hôm nay",month:"Tháng",week:"Tuần",day:"Ngày"},"id-ID":{today:"Hari ini",month:"Bulan",week:"Minggu",day:"Hari"},"ms-MY":{today:"Hari ini",month:"Bulan",week:"Minggu",day:"Hari"},"uk-UA":{today:"Сьогодні",month:"Місяць",week:"Тиждень",day:"День"}};function v(t,e){const a=t.split("-")[0];return(u[t]||u[a]||u["en-US"])[e]}function y(t){return!!t.allDay||!n(t.start,t.end)}function g(a,s){const o=t(s[0]),r=e(s[s.length-1]),c=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}));c.sort((t,e)=>{const a=t.start-e.start;if(0!==a)return a;const n=i(t._visStart,t._visEnd);return i(e._visStart,e._visEnd)-n||(t._origStart||t.start)-(e._origStart||e.start)});const l=[],d=[];for(const t of c){const e=s.findIndex(e=>n(e,t._visStart)),a=s.findIndex(e=>n(e,t._visEnd)),i=-1===e?0:e,o=-1===a?s.length-1:a;let r=l.findIndex(t=>t<=i);-1===r?(r=l.length,l.push(o+1)):l[r]=o+1,d.push({event:t,startCol:i,endCol:o,slot:r,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 n(t,e){return t.getFullYear()===e.getFullYear()&&t.getMonth()===e.getMonth()&&t.getDate()===e.getDate()}function s(t){return n(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,n){const s=e.getDay(),i=t(a(e,-((s-n+7)%7)));return Array.from({length:7},(t,e)=>a(i,e))}function c(t,e,n){const s=new Date(t,e,1),i=new Date(t,e+1,0),o=(s.getDay()-n+7)%7,r=7*Math.ceil((o+i.getDate())/7),c=a(s,-o);return Array.from({length:r},(t,e)=>a(c,e))}function l(t,e,a){const n={hour:"numeric",hour12:!a};return 0!==t.getMinutes()&&(n.minute="2-digit"),new Intl.DateTimeFormat(e,n).format(t)}function d(t,e,a){return Array.from({length:7},(n,s)=>{const i=new Date(2025,0,5+(e+s)%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"},"en-GB":{today:"Today",month:"Month",week:"Week",day:"Day"},"es-ES":{today:"Hoy",month:"Mes",week:"Semana",day:"Día"},"es-MX":{today:"Hoy",month:"Mes",week:"Semana",day:"Día"},"fr-FR":{today:"Aujourd'hui",month:"Mois",week:"Semaine",day:"Jour"},"fr-CA":{today:"Aujourd'hui",month:"Mois",week:"Semaine",day:"Jour"},"de-DE":{today:"Heute",month:"Monat",week:"Woche",day:"Tag"},"it-IT":{today:"Oggi",month:"Mese",week:"Settimana",day:"Giorno"},"pt-PT":{today:"Hoje",month:"Mês",week:"Semana",day:"Dia"},"pt-BR":{today:"Hoje",month:"Mês",week:"Semana",day:"Dia"},"nl-NL":{today:"Vandaag",month:"Maand",week:"Week",day:"Dag"},"pl-PL":{today:"Dzisiaj",month:"Miesiąc",week:"Tydzień",day:"Dzień"},"ru-RU":{today:"Сегодня",month:"Месяц",week:"Неделя",day:"День"},"tr-TR":{today:"Bugün",month:"Ay",week:"Hafta",day:"Gün"},"sv-SE":{today:"Idag",month:"Månad",week:"Vecka",day:"Dag"},"da-DK":{today:"I dag",month:"Måned",week:"Uge",day:"Dag"},"fi-FI":{today:"Tänään",month:"Kuukausi",week:"Viikko",day:"Päivä"},"no-NO":{today:"I dag",month:"Måned",week:"Uke",day:"Dag"},"cs-CZ":{today:"Dnes",month:"Měsíc",week:"Týden",day:"Den"},"hu-HU":{today:"Ma",month:"Hónap",week:"Hét",day:"Nap"},"ro-RO":{today:"Astăzi",month:"Lună",week:"Săptămână",day:"Zi"},"el-GR":{today:"Σήμερα",month:"Μήνας",week:"Εβδομάδα",day:"Ημέρα"},"ja-JP":{today:"今日",month:"月",week:"週",day:"日"},"ko-KR":{today:"오늘",month:"월",week:"주",day:"일"},"zh-CN":{today:"今天",month:"月",week:"周",day:"日"},"zh-TW":{today:"今天",month:"月",week:"週",day:"日"},"ar-SA":{today:"اليوم",month:"شهر",week:"أسبوع",day:"يوم"},"he-IL":{today:"היום",month:"חודש",week:"שבוע",day:"יום"},"hi-IN":{today:"आज",month:"महीना",week:"सप्ताह",day:"दिन"},"th-TH":{today:"วันนี้",month:"เดือน",week:"สัปดาห์",day:"วัน"},"vi-VN":{today:"Hôm nay",month:"Tháng",week:"Tuần",day:"Ngày"},"id-ID":{today:"Hari ini",month:"Bulan",week:"Minggu",day:"Hari"},"ms-MY":{today:"Hari ini",month:"Bulan",week:"Minggu",day:"Hari"},"uk-UA":{today:"Сьогодні",month:"Місяць",week:"Тиждень",day:"День"}};function v(t,e){const a=t.split("-")[0];return(u[t]||u[a]||u["en-US"])[e]}function p(t){return!!t.allDay||!n(t.start,t.end)}function y(a,s){const o=t(s[0]),r=e(s[s.length-1]),c=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}));c.sort((t,e)=>{const a=t.start-e.start;if(0!==a)return a;const n=i(t._visStart,t._visEnd);return i(e._visStart,e._visEnd)-n||(t._origStart||t.start)-(e._origStart||e.start)});const l=[],d=[];for(const t of c){const e=s.findIndex(e=>n(e,t._visStart)),a=s.findIndex(e=>n(e,t._visEnd)),i=-1===e?0:e,o=-1===a?s.length-1:a;let r=l.findIndex(t=>t<=i);-1===r?(r=l.length,l.push(o+1)):l[r]=o+1,d.push({event:t,startCol:i,endCol:o,slot:r,isStart:t._isStart,isEnd:t._isEnd})}return d}
11
10
  /* ============================================================
12
11
  ES MODULE EXPORT
13
12
  Also available as window.SimpleCalendarJs via the IIFE wrapper.
14
13
  ============================================================ */
15
- 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",use24Hour:!1,showTimeInItems:!0,showGridLines:!0,showToolbar:!0,showTodayButton:!0,showNavigation:!0,showTitle:!0,showYearPicker:!0,showViewSwitcher:!0,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._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._el.appendChild(this._root),this._onClick=this._handleClick.bind(this),this._root.addEventListener("click",this._onClick),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()}destroy(){this._nowInterval&&clearInterval(this._nowInterval),this._yearOutsideHandler&&document.removeEventListener("click",this._yearOutsideHandler),this._root.removeEventListener("click",this._onClick),this._root.remove()}_getRange(){if("month"===this._view){const a=c(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])}}return{start:t(this._date),end:e(this._date)}}async _fetchAndRender(){if(this._root.innerHTML=this._buildShell(),!this._opts.fetchEvents)return void this._renderView();const t=this._root.querySelector(".uc-loading");t&&(t.style.display="flex");const e=this._getRange();try{const t=await this._opts.fetchEvents(e.start,e.end);this._events=(t||[]).map(t=>({...t,start:o(t.start),end:o(t.end)}))}catch(t){this._events=[]}t&&(t.style.display="none"),this._renderView()}_renderView(){const t=this._root.querySelector(".uc-view-container");if(t)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 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;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 n=t=>new Intl.DateTimeFormat(a,{month:"short",day:"numeric"}).format(t);return`${n(t)} – ${n(e)}`}(t[0],t[6],this._opts.locale)}else a=new Intl.DateTimeFormat(this._opts.locale,{weekday:"long",month:"long",day:"numeric"}).format(this._date);let s="";if(this._opts.showYearPicker&&this._yearPickerOpen){const a=this._yearPickerBase,n=Array.from({length:12},(n,s)=>{const i=a+s,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("");s=`\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">${n}</div>\n </div>`}const i=this._opts.locale,o=v(i,"today"),c=v(i,"month"),l=v(i,"week"),d=v(i,"day");let u="";if(this._opts.showNavigation||this._opts.showTodayButton){const t=this._opts.showNavigation?'<button class="uc-btn uc-nav-btn" data-action="prev" aria-label="Previous">&#8249;</button>':"",e=new Date,a=n(this._date,e)?" uc-active":"";u=`\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(o)}</button>`:""}${this._opts.showNavigation?'<button class="uc-btn uc-nav-btn" data-action="next" aria-label="Next">&#8250;</button>':""}\n </div>`}let y="";if(this._opts.showTitle){const e=this._opts.showYearPicker?`<button class="uc-year-btn${this._yearPickerOpen?" uc-open":""}" data-action="year-pick" aria-label="Select year">${t}</button>`:t;y=`\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 ${s}\n </div>`}let g="";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(l)}</button>`),t.includes("day")&&e.push(`<button class="uc-btn uc-view-btn${"day"===this._view?" uc-active":""}" data-view="day">${h(d)}</button>`),e.length>0&&(g=`\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 ${u}${y}${g}\n </div>\n `}_buildMonthView(){const{locale:a,weekStartsOn:n}=this._opts,s=c(this._date.getFullYear(),this._date.getMonth(),n),i=d(a,n,"short"),o=this._events.map(a=>({...a,_origStart:a.start,start:y(a)?t(a.start):a.start,end:y(a)?e(a.end):a.end})),r=i.map(t=>`<div class="uc-month-day-name">${h(t)}</div>`).join(""),l=[];for(let t=0;t<s.length;t+=7)l.push(s.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">${l.map(t=>this._buildWeekRow(t,o)).join("")}</div>\n </div>\n `}_buildWeekRow(t,e){const a=this._date.getMonth(),i=e.filter(y),o=e.filter(t=>!y(t)),r=g(i,t),c=Array.from({length:7},()=>new Set);for(const{startCol:t,endCol:e,slot:a}of r)for(let n=t;n<=e;n++)c[n].add(a);const d=t.map(t=>o.filter(e=>n(e.start,t)).sort((t,e)=>t.start-e.start)),u=t.map((t,e)=>`\n <div class="uc-day-cell${s(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 v="";for(const{event:t,startCol:e,endCol:a,slot:n,isStart:s,isEnd:i}of r){if(n>=3)continue;const o=100/7,r=e*o,c=(a-e+1)*o,l=`calc(var(--cal-header-day-height) + ${n} * (var(--cal-event-height) + var(--cal-event-gap)) + 4px)`,d=t.color||"var(--cal-event-bg)",u=s?"var(--cal-event-border-radius)":"0",y=i?"var(--cal-event-border-radius)":"0";v+=`\n <div class="uc-event-bar${s?"":" uc-continues-left"}${i?"":" uc-continues-right"}"\n style="left:calc(${r}% + 2px);width:calc(${c}% - 4px);top:${l};background:${h(d)};border-radius:${u} ${y} ${y} ${u};"\n data-event-id="${h(t.id)}" data-action="event-click" title="${h(t.title)}">\n ${s?`<span class="uc-event-title">${h(t.title)}</span>`:"&nbsp;"}\n </div>`}for(let e=0;e<7;e++){const a=100/7,n=e*a,s=t[e],i=([...c[e]].filter(t=>t<3).length,[...c[e]].filter(t=>t>=3).length),o=[...c[e]],r=o.length>0?Math.max(...o)+1:0,u=[];for(let t=r;t<3;t++)u.push(t);const y=d[e];let g=i;if(y.forEach((t,e)=>{if(e<u.length){const s=`calc(var(--cal-header-day-height) + ${u[e]} * (var(--cal-event-height) + var(--cal-event-gap)) + 4px)`,i=t.color||"var(--cal-event-bg)",o=l(t.start,this._opts.locale,this._opts.use24Hour),r=this._opts.showTimeInItems?`<span class="uc-event-time">${h(o)}</span>`:"";v+=`\n <div class="uc-event-bar"\n style="left:calc(${n}% + 2px);width:calc(${a}% - 4px);top:${s};background:${h(i)};"\n data-event-id="${h(t.id)}" data-action="event-click" title="${h(t.title)}">\n ${r}\n <span class="uc-event-title">${h(t.title)}</span>\n </div>`}else g++}),g>0){v+=`\n <div class="uc-more-link"\n style="left:calc(${n}% + 2px);width:calc(${a}% - 4px);top:${"calc(var(--cal-header-day-height) + 3 * (var(--cal-event-height) + var(--cal-event-gap)) + 4px)"};"\n data-date="${s.toISOString()}" data-action="more-click">\n +${g} more\n </div>`}}return`\n <div class="uc-week-row">\n <div class="uc-week-cells">${u}</div>\n <div class="uc-week-events">${v}</div>\n </div>`}_buildWeekOrDayView(a){const{locale:i,weekStartsOn:o,use24Hour:r}=this._opts,c=1===a.length,u=c?" uc-day-header-single":"",v=c?[new Intl.DateTimeFormat(i,{weekday:"short"}).format(a[0])]:d(i,o,"short"),p=t(a[0]),_=e(a[a.length-1]),w=this._events.filter(t=>y(t)&&t.start<=_&&e(t.end)>=p).map(a=>({...a,start:t(a.start),end:e(a.end)})),m=this._events.filter(t=>!y(t)&&t.start>=p&&t.start<=_),k=a.map((t,e)=>{const a=s(t)?" uc-today":"",n=t.getDate();return`\n <div class="uc-week-day-header${a}">\n <span class="uc-week-day-name">${h(v[e])}</span>\n <span class="uc-week-day-num" data-action="day-number" data-date="${t.toISOString()}">${n}</span>\n </div>`}).join(""),f=c?w.map((t,e)=>({event:t,startCol:0,endCol:0,slot:e,isStart:!0,isEnd:!0})):g(w,a),$=f.length?Math.max(...f.map(t=>t.slot))+1:0;let b="";for(const{event:t,startCol:e,endCol:n,slot:s,isStart:i,isEnd:o}of f){const r=100/a.length,c=i?"var(--cal-event-border-radius)":"0",l=o?"var(--cal-event-border-radius)":"0";b+=`\n <div class="uc-event-bar${i?"":" uc-continues-left"}${o?"":" uc-continues-right"}"\n style="left:calc(${e*r}% + 2px);width:calc(${(n-e+1)*r}% - 4px);top:${`calc(${s} * (var(--cal-event-height) + 3px) + 2px)`};background:${h(t.color||"var(--cal-event-bg)")};border-radius:${c} ${l} ${l} ${c};"\n data-event-id="${h(t.id)}" data-action="event-click" title="${h(t.title)}">\n ${i?`<span class="uc-event-title">${h(t.title)}</span>`:"&nbsp;"}\n </div>`}const D=`calc(${Math.max(1,$)} * (var(--cal-event-height) + 3px) + 6px)`,S=new Date,T=60*S.getHours()+S.getMinutes(),M=a.length;return`\n <div class="${c?"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}">${k}</div>\n </div>\n <div class="uc-all-day-section">\n <div class="uc-time-gutter-spacer uc-all-day-label">all-day</div>\n <div class="uc-all-day-events" style="min-height:${D}">${b}</div>\n </div>\n <div class="uc-time-body">\n <div class="uc-time-grid-inner">\n <div class="uc-time-gutter">${Array.from({length:24},(t,e)=>`<div class="uc-hour-cell"><span class="uc-hour-label">${h(0===e?"":l(new Date(2e3,0,1,e),"en-US",r))}</span></div>`).join("")}</div>\n <div class="uc-time-columns" style="--uc-col-count:${M}">${a.map(t=>{const e=s(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=[],n=[];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,n.push({event:t,col:e})}return n.map(t=>{const e=n.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}})}(m.filter(e=>n(e.start,t))).map(({event:t,col:e,totalCols:a})=>{const n=60*t.start.getHours()+t.start.getMinutes(),s=60*t.end.getHours()+t.end.getMinutes(),o=`calc(${n} / 60 * var(--cal-hour-height))`,c=`calc(${Math.max(s-n,30)} / 60 * var(--cal-hour-height))`,d=100/a,u=`calc(${e*d}% + 1px)`,v=`calc(${d}% - 2px)`,y=t.color||"var(--cal-event-bg)",g=l(t.start,i,r),p=s-n<=60?" uc-timed-event--short":"",_=this._opts.showTimeInItems?`<span class="uc-event-time">${h(g)}</span>`:"";return`\n <div class="uc-timed-event${p}"\n style="top:${o};height:${c};left:${u};width:${v};background:${h(y)};"\n data-event-id="${h(t.id)}" data-action="event-click" title="${h(t.title)}">\n ${_}\n <span class="uc-event-title">${h(t.title)}</span>\n </div>`}).join(""),c=s(t)?`<div class="uc-now-indicator" style="top:calc(${T} / 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}${c}\n </div>`}).join("")}</div>\n </div>\n </div>\n </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 n=e.target.closest("[data-action]");if(!n)return;switch(n.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(n.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=n.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(n.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(n.dataset.date);this._date=t(a),this.setView("day");break}case"more-click":{if(e.stopPropagation(),!this._opts.enabledViews.includes("day"))break;const a=new Date(n.dataset.date);this._date=t(a),this.setView("day");break}case"slot-col":if(e.target.closest('[data-action="event-click"]'))break;if(this._opts.onSlotClick){const t=n,a=t.getBoundingClientRect(),s=e.clientY-a.top,i=parseFloat(getComputedStyle(this._root).getPropertyValue("--cal-hour-height"))||60,o=Math.max(0,Math.floor(s/i*60)),r=Math.floor(o/60)%24,c=15*Math.round(o%60/15),l=new Date(t.dataset.date);l.setHours(r,c,0,0),this._opts.onSlotClick(l,e)}}}_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,showToolbar:!0,showTodayButton:!0,showNavigation:!0,showTitle:!0,showYearPicker:!0,showViewSwitcher:!0,showTooltips:!0,showBorder:!0,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._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()}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=c(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])}}return{start:t(this._date),end:e(this._date)}}async _fetchAndRender(){if(this._root.innerHTML=this._buildShell(),!this._opts.fetchEvents)return void this._renderView();const t=this._root.querySelector(".uc-loading");t&&(t.style.display="flex");const e=this._getRange();try{const t=await this._opts.fetchEvents(e.start,e.end);this._events=(t||[]).map(t=>({...t,start:o(t.start),end:o(t.end)}))}catch(t){this._events=[]}t&&(t.style.display="none"),this._renderView()}_renderView(){const t=this._root.querySelector(".uc-view-container");if(t)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 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;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 n=t=>new Intl.DateTimeFormat(a,{month:"short",day:"numeric"}).format(t);return`${n(t)} – ${n(e)}`}(t[0],t[6],this._opts.locale)}else a=new Intl.DateTimeFormat(this._opts.locale,{weekday:"long",month:"long",day:"numeric"}).format(this._date);let s="";if(this._opts.showYearPicker&&this._yearPickerOpen){const a=this._yearPickerBase,n=Array.from({length:12},(n,s)=>{const i=a+s,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("");s=`\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">${n}</div>\n </div>`}const i=this._opts.locale,o=v(i,"today"),c=v(i,"month"),l=v(i,"week"),d=v(i,"day");let u="";if(this._opts.showNavigation||this._opts.showTodayButton){const t=this._opts.showNavigation?'<button class="uc-btn uc-nav-btn" data-action="prev" aria-label="Previous">&#8249;</button>':"",e=new Date,a=n(this._date,e)?" uc-active":"";u=`\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(o)}</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=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 ${s}\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(c)}</button>`),t.includes("week")&&e.push(`<button class="uc-btn uc-view-btn${"week"===this._view?" uc-active":""}" data-view="week">${h(l)}</button>`),t.includes("day")&&e.push(`<button class="uc-btn uc-view-btn${"day"===this._view?" uc-active":""}" data-view="day">${h(d)}</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 ${u}${p}${y}\n </div>\n `}_buildMonthView(){const{locale:a,weekStartsOn:n}=this._opts,s=c(this._date.getFullYear(),this._date.getMonth(),n),i=d(a,n,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})),r=i.map(t=>`<div class="uc-month-day-name">${h(t)}</div>`).join(""),l=[];for(let t=0;t<s.length;t+=7)l.push(s.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">${l.map(t=>this._buildWeekRow(t,o)).join("")}</div>\n </div>\n `}_buildWeekRow(t,e){const a=this._date.getMonth(),i=e.filter(p),o=e.filter(t=>!p(t)),r=y(i,t),c=Array.from({length:7},()=>new Set);for(const{startCol:t,endCol:e,slot:a}of r)for(let n=t;n<=e;n++)c[n].add(a);const d=t.map(t=>o.filter(e=>n(e.start,t)).sort((t,e)=>t.start-e.start)),u=t.map((t,e)=>`\n <div class="uc-day-cell${s(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 v="";for(const{event:t,startCol:e,endCol:a,slot:n,isStart:s,isEnd:i}of r){if(n>=3)continue;const o=100/7,r=e*o,c=(a-e+1)*o,l=`calc(var(--cal-header-day-height) + ${n} * (var(--cal-event-height) + var(--cal-event-gap)) + 4px)`,d=t.color||"var(--cal-event-bg)",u=s?"var(--cal-event-border-radius)":"0",p=i?"var(--cal-event-border-radius)":"0",y=s?"":" uc-continues-left",_=i?"":" uc-continues-right",w=this._opts.showTooltips?`data-tooltip="${h(t.tooltip||t.description||t.title)}"`:"";v+=`\n <div class="uc-event-bar${y}${_}"\n style="left:calc(${r}% + 2px);width:calc(${c}% - 4px);top:${l};background:${h(d)};border-radius:${u} ${p} ${p} ${u};"\n data-event-id="${h(t.id)}" data-action="event-click"\n ${w}>\n ${s?`<span class="uc-event-title">${h(t.title)}</span>`:"&nbsp;"}\n </div>`}for(let e=0;e<7;e++){const a=100/7,n=e*a,s=t[e],i=([...c[e]].filter(t=>t<3).length,[...c[e]].filter(t=>t>=3).length),o=[...c[e]],r=o.length>0?Math.max(...o)+1:0,u=[];for(let t=r;t<3;t++)u.push(t);const p=d[e];let y=i;if(p.forEach((t,e)=>{if(e<u.length){const s=`calc(var(--cal-header-day-height) + ${u[e]} * (var(--cal-event-height) + var(--cal-event-gap)) + 4px)`,i=t.color||"var(--cal-event-bg)",o=l(t.start,this._opts.locale,this._opts.use24Hour),r=this._opts.showTimeInItems?`<span class="uc-event-time">${h(o)}</span>`:"",c=this._opts.showTooltips?`data-tooltip="${h(t.tooltip||t.description||t.title)}"`:"";v+=`\n <div class="uc-event-bar"\n style="left:calc(${n}% + 2px);width:calc(${a}% - 4px);top:${s};background:${h(i)};"\n data-event-id="${h(t.id)}" data-action="event-click"\n ${c}>\n ${r}\n <span class="uc-event-title">${h(t.title)}</span>\n </div>`}else y++}),y>0){v+=`\n <div class="uc-more-link"\n style="left:calc(${n}% + 2px);width:calc(${a}% - 4px);top:${"calc(var(--cal-header-day-height) + 3 * (var(--cal-event-height) + var(--cal-event-gap)) + 2px)"};"\n data-date="${s.toISOString()}" data-action="more-click">\n +${y} more\n </div>`}}return`\n <div class="uc-week-row">\n <div class="uc-week-cells">${u}</div>\n <div class="uc-week-events">${v}</div>\n </div>`}_buildWeekOrDayView(a){const{locale:i,weekStartsOn:o,use24Hour:r}=this._opts,c=1===a.length,u=c?" uc-day-header-single":"",v=c?[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]),g=this._events.filter(t=>p(t)&&t.start<=w&&e(t.end)>=_).map(a=>({...a,start:t(a.start),end:e(a.end)})),m=this._events.filter(t=>!p(t)&&t.start>=_&&t.start<=w),k=a.map((t,e)=>{const a=s(t)?" uc-today":"",n=t.getDate();return`\n <div class="uc-week-day-header${a}">\n <span class="uc-week-day-name">${h(v[e])}</span>\n <span class="uc-week-day-num" data-action="day-number" data-date="${t.toISOString()}">${n}</span>\n </div>`}).join(""),f=c?g.map((t,e)=>({event:t,startCol:0,endCol:0,slot:e,isStart:!0,isEnd:!0})):y(g,a),$=f.length?Math.max(...f.map(t=>t.slot))+1:0;let b="";for(const{event:t,startCol:e,endCol:n,slot:s,isStart:i,isEnd:o}of f){const r=100/a.length,c=e*r,l=(n-e+1)*r,d=`calc(${s} * (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",_=o?"":" uc-continues-right",w=this._opts.showTooltips?`data-tooltip="${h(t.tooltip||t.description||t.title)}"`:"";b+=`\n <div class="uc-event-bar${y}${_}"\n style="left:calc(${c}% + 2px);width:calc(${l}% - 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 ${i?`<span class="uc-event-title">${h(t.title)}</span>`:"&nbsp;"}\n </div>`}const D=`calc(${Math.max(1,$)} * (var(--cal-event-height) + 3px) + 6px)`,S=new Date,T=60*S.getHours()+S.getMinutes(),M=a.length;return`\n <div class="${c?"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}">${k}</div>\n </div>\n <div class="uc-all-day-section">\n <div class="uc-time-gutter-spacer uc-all-day-label">all-day</div>\n <div class="uc-all-day-events" style="min-height:${D}">${b}</div>\n </div>\n <div class="uc-time-body">\n <div class="uc-time-grid-inner">\n <div class="uc-time-gutter">${Array.from({length:24},(t,e)=>`<div class="uc-hour-cell"><span class="uc-hour-label">${h(0===e?"":l(new Date(2e3,0,1,e),"en-US",r))}</span></div>`).join("")}</div>\n <div class="uc-time-columns" style="--uc-col-count:${M}">${a.map(t=>{const e=s(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=[],n=[];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,n.push({event:t,col:e})}return n.map(t=>{const e=n.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}})}(m.filter(e=>n(e.start,t))).map(({event:t,col:e,totalCols:a})=>{const n=60*t.start.getHours()+t.start.getMinutes(),s=60*t.end.getHours()+t.end.getMinutes(),o=`calc(${n} / 60 * var(--cal-hour-height))`,c=`calc(${Math.max(s-n,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=l(t.start,i,r),_=s-n<=60?" uc-timed-event--short":"",w=this._opts.showTimeInItems?`<span class="uc-event-time">${h(y)}</span>`:"",g=this._opts.showTooltips?`data-tooltip="${h(t.tooltip||t.description||t.title)}"`:"";return`\n <div class="uc-timed-event${_}"\n style="top:${o};height:${c};left:${u};width:${v};background:${h(p)};"\n data-event-id="${h(t.id)}" data-action="event-click"\n ${g}>\n ${w}\n <span class="uc-event-title">${h(t.title)}</span>\n </div>`}).join(""),c=s(t)?`<div class="uc-now-indicator" style="top:calc(${T} / 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}${c}\n </div>`}).join("")}</div>\n </div>\n </div>\n </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 n=e.target.closest("[data-action]");if(!n)return;switch(n.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(n.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=n.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(n.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(n.dataset.date);this._date=t(a),this.setView("day");break}case"more-click":{if(e.stopPropagation(),!this._opts.enabledViews.includes("day"))break;const a=new Date(n.dataset.date);this._date=t(a),this.setView("day");break}case"slot-col":if(e.target.closest('[data-action="event-click"]'))break;if(this._opts.onSlotClick){const t=n,a=t.getBoundingClientRect(),s=e.clientY-a.top,i=parseFloat(getComputedStyle(this._root).getPropertyValue("--cal-hour-height"))||60,o=Math.max(0,Math.floor(s/i*60)),r=Math.floor(o/60)%24,c=15*Math.round(o%60/15),l=new Date(t.dataset.date);l.setHours(r,c,0,0),this._opts.onSlotClick(l,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(),n=window.innerWidth;e.classList.remove("uc-tooltip-left","uc-tooltip-right","uc-tooltip-bottom");const s=a.width/2-125;a.left+s+250>n-10?e.classList.add("uc-tooltip-left"):a.left+s<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)}}});
@@ -1,5 +1,5 @@
1
1
  /**
2
- * SimpleCalendarJs v3.0.0 — Angular Wrapper
2
+ * SimpleCalendarJs v3.0.2 — 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.0 — React Wrapper
2
+ * SimpleCalendarJs v3.0.2 — 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.0 — Vue 3 Wrapper
2
+ * SimpleCalendarJs v3.0.2 — 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.0",
3
+ "version": "3.0.2",
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",
@@ -15,10 +15,6 @@
15
15
  "build": "node build.js",
16
16
  "prepublishOnly": "npm run build"
17
17
  },
18
- "repository": {
19
- "type": "git",
20
- "url": "git+https://github.com/pclslopes/SimpleCalendarJs.git"
21
- },
22
18
  "keywords": [
23
19
  "calendar",
24
20
  "javascript",
@@ -42,7 +38,7 @@
42
38
  "author": "Pedro Lopes <simplecalendarjs@gmail.com> (https://www.simplecalendarjs.com)",
43
39
  "license": "SEE LICENSE IN LICENSE",
44
40
  "bugs": {
45
- "url": "https://github.com/yourusername/SimpleCalendarJs/issues"
41
+ "email": "simplecalendarjs@gmail.com"
46
42
  },
47
43
  "homepage": "https://www.simplecalendarjs.com",
48
44
  "devDependencies": {