simple-calendar-js 3.0.12 → 3.0.13

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
@@ -3,7 +3,7 @@
3
3
  A lightweight, zero-dependency JavaScript calendar component with internationalization support and framework wrappers for React, Vue, and Angular.
4
4
 
5
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)](https://unpkg.com/simple-calendar-js/LICENSE)
6
+ [![license](https://img.shields.io/npm/l/simple-calendar-js)](https://github.com/pclslopes/SimpleCalendarJs/blob/main/LICENSE)
7
7
 
8
8
  ## Features
9
9
 
@@ -15,6 +15,7 @@ A lightweight, zero-dependency JavaScript calendar component with internationali
15
15
  - **Module System Support** - Works as UMD (CommonJS, AMD, ES modules, or browser global)
16
16
  - **Dark Mode Ready** - Automatic dark theme detection and support
17
17
  - **Async Event Loading** - Fetch events dynamically with `async/await` support
18
+ - **Drag & Drop** - Move and resize events with mouse or touch
18
19
  - **Responsive Design** - Adapts to any screen size
19
20
  - **Customizable Styling** - CSS custom properties for easy theming
20
21
  - **Accessible** - Semantic HTML with proper ARIA attributes
@@ -94,7 +95,7 @@ npm install simple-calendar-js
94
95
 
95
96
  ### Manual Download
96
97
 
97
- Download the files from the [dist/](dist/) folder and include them in your project.
98
+ Download the files from the [dist/](https://github.com/pclslopes/SimpleCalendarJs/tree/main/dist) folder and include them in your project.
98
99
 
99
100
  ## Quick Start
100
101
 
@@ -213,1454 +214,124 @@ export class CalendarComponent {
213
214
  }
214
215
  ```
215
216
 
216
- ## Picker Modes
217
-
218
- SimpleCalendarJs includes built-in date selection functionality with two picker modes:
219
-
220
- ### Date Picker Mode
221
-
222
- Select a single date:
223
-
224
- ```javascript
225
- const datePicker = new SimpleCalendarJs('#date-picker', {
226
- mode: 'date-picker',
227
- onDateSelect: (date) => {
228
- console.log('Selected date:', date);
229
- // Update your form, input field, etc.
230
- }
231
- });
232
-
233
- // Programmatic selection
234
- datePicker.setSelectedDate(new Date('2024-12-25'));
235
-
236
- // Get selected date
237
- const selected = datePicker.getSelectedDate();
238
- ```
239
-
240
- ### Range Picker Mode
241
-
242
- Select a date range (start and end dates):
243
-
244
- ```javascript
245
- const rangePicker = new SimpleCalendarJs('#range-picker', {
246
- mode: 'range-picker',
247
- onRangeSelect: (startDate, endDate) => {
248
- console.log('Selected range:', startDate, 'to', endDate);
249
- // Update your booking form, filter dates, etc.
250
- }
251
- });
252
-
253
- // Programmatic selection
254
- rangePicker.setSelectedRange(
255
- new Date('2024-12-01'),
256
- new Date('2024-12-07')
257
- );
258
-
259
- // Get selected range
260
- const range = rangePicker.getSelectedRange(); // { start: Date, end: Date }
261
-
262
- // Clear selection
263
- rangePicker.clearSelection();
264
- ```
265
-
266
- ### Picker Mode Behavior
267
-
268
- When using picker modes:
269
- - **No events displayed** - Events are not fetched or rendered
270
- - **Month view only** - View switcher is automatically hidden
271
- - **Centered day numbers** - Day numbers are always centered (ignores `monthDayNumberAlign`)
272
- - **No today highlight** - Today's date is not highlighted to avoid confusion with selection
273
- - **Visual feedback** - Selected dates are highlighted with clear visual indicators
274
- - **Smart range selection** - In range-picker mode, dates are automatically ordered
275
-
276
- ## Configuration Options
277
-
278
- | Option | Type | Default | Description |
279
- |--------|------|---------|-------------|
280
- | `mode` | string | `'calendar'` | Calendar mode: `'calendar'` (standard calendar with events), `'date-picker'` (single date selection), or `'range-picker'` (date range selection) |
281
- | `defaultView` | string | `'month'` | Initial view: `'month'`, `'week'`, `'day'`, or `'list'` |
282
- | `defaultDate` | Date | `new Date()` | Initial date to display |
283
- | `weekStartsOn` | number | `0` | First day of week: `0` (Sunday) or `1` (Monday) |
284
- | `locale` | string | `'default'` | Locale code for Intl API (e.g., `'en-US'`, `'fr-FR'`, `'ja-JP'`) |
285
- | `weekdayFormat` | string | `'short'` | Weekday name format: `'narrow'` (1-2 letters), `'short'` (abbreviated), or `'long'` (full name) |
286
- | `use24Hour` | boolean | `false` | Use 24-hour time format |
287
- | `showTimeInItems` | boolean | `true` | Show time in event items |
288
- | `showGridLines` | boolean | `true` | Show calendar grid lines |
289
- | `showBorder` | boolean | `true` | Show calendar outer border |
290
- | `showToolbar` | boolean | `true` | Show the toolbar |
291
- | `showTodayButton` | boolean | `true` | Show "Today" button |
292
- | `showNavigation` | boolean | `true` | Show prev/next navigation arrows |
293
- | `showTitle` | boolean | `true` | Show month/year title |
294
- | `showYearPicker` | boolean | `true` | Enable year picker dropdown (month view) |
295
- | `showViewSwitcher` | boolean | `true` | Show view switcher buttons |
296
- | `showTooltips` | boolean | `true` | Show tooltips on hover for events |
297
- | `tooltipAllowHtml` | boolean | `true` | Allow HTML in tooltips (sanitized for security). Supports: `<b>`, `<i>`, `<strong>`, `<em>`, `<span>`, `<br>`, `<a>` with safe attributes and URL validation |
298
- | `tooltipBgColor` | string \| null | `null` | Custom tooltip background color (hex). If `null`, uses CSS variable `--cal-tooltip-bg` |
299
- | `tooltipTextColor` | string \| null | `null` | Custom tooltip text color (hex). If `null`, uses CSS variable `--cal-tooltip-text` |
300
- | `tooltipMaxWidth` | number | `250` | Maximum tooltip width in pixels |
301
- | `tooltipDelay` | number | `400` | Delay in milliseconds before tooltip appears on hover |
302
- | `listDaysForward` | number | `30` | Number of days forward to show in list view |
303
- | `enabledViews` | string[] | `['month', 'week', 'day']` | Available view modes. Add `'list'` to enable list view |
304
- | `enableDragDrop` | boolean | `false` | Enable drag and drop to move events |
305
- | `enableResize` | boolean | `false` | Enable resizing events to change duration |
306
- | `autoContrastText` | boolean | `false` | Automatically calculate contrasting text color based on event background color for better readability |
307
- | `contrastLevel` | string | `'high'` | Text contrast level when `autoContrastText` is enabled: `'high'` (black/white), `'medium'` (darker/lighter shade), `'low'` (subtle variation) |
308
- | `allowHtmlInEvents` | boolean | `true` | Allow basic HTML tags in event titles (sanitized for security). Supports: `<b>`, `<i>`, `<strong>`, `<em>`, `<span>`, `<br>` with `class` attribute for icons |
309
- | `monthTimedEventStyle` | string | `'list'` | Display style for timed events in month view: `'list'` (schedule format) or `'block'` (traditional blocks) |
310
- | `monthDayNumberAlign` | string | `'left'` | Horizontal alignment of day numbers in month view cells: `'left'`, `'center'`, or `'right'` |
311
- | `showEventBorder` | boolean | `false` | Display borders around events. Border color adaptively darkens from event background (light colors darken more for visibility), or use event's `borderColor` property |
312
- | `fetchEvents` | function | `null` | Async function to fetch events: `async (start, end) => Event[]` |
313
- | `onEventClick` | function | `null` | Callback when event is clicked: `(event, mouseEvent) => void` |
314
- | `onSlotClick` | function | `null` | Callback when time slot is clicked: `(date, mouseEvent) => void` |
315
- | `onDateSelect` | function | `null` | Callback when date is selected in date-picker mode: `(date) => void` |
316
- | `onRangeSelect` | function | `null` | Callback when date range is selected in range-picker mode: `(startDate, endDate) => void` |
317
- | `onViewChange` | function | `null` | Callback when view changes: `(view) => void` |
318
- | `onNavigate` | function | `null` | Callback when date range changes: `(startDate, endDate) => void` |
319
- | `onEventDrop` | function | `null` | Callback when event is dropped: `(event, oldStart, oldEnd, newStart, newEnd) => void` |
320
-
321
- ## Event Object Format
322
-
323
- Events returned by `fetchEvents` should follow this structure:
324
-
325
- ```typescript
326
- interface CalendarEvent {
327
- id: string | number; // Unique identifier
328
- title: string; // Event title (can contain HTML if allowHtmlInEvents is enabled)
329
- start: Date; // Start date/time
330
- end?: Date; // End date/time (optional, defaults to start)
331
- allDay?: boolean; // All-day event flag (optional)
332
- color?: string; // Custom background color (hex, optional)
333
- textColor?: string; // Custom text color (hex, optional) - overrides autoContrastText
334
- allowHtml?: boolean; // Override global allowHtmlInEvents setting for this event (optional)
335
- description?: string; // Event description (also used for tooltip if tooltip not provided)
336
- tooltip?: string; // Custom tooltip text shown on hover (can contain HTML if tooltipAllowHtml is enabled)
337
- tooltipAllowHtml?: boolean; // Override global tooltipAllowHtml setting for this event's tooltip (optional)
338
- [key: string]: any; // Any additional custom properties
339
- }
340
- ```
341
-
342
- ### Example Events
343
-
344
- ```javascript
345
- const events = [
346
- {
347
- id: 1,
348
- title: 'Team Meeting',
349
- start: new Date('2024-03-15T10:00:00'),
350
- end: new Date('2024-03-15T11:00:00'),
351
- color: '#3b82f6',
352
- tooltip: 'Weekly team sync\nDiscuss Q1 roadmap and sprint planning'
353
- },
354
- {
355
- id: 2,
356
- title: 'Conference',
357
- start: new Date('2024-03-20T00:00:00'),
358
- end: new Date('2024-03-22T23:59:59'),
359
- allDay: true,
360
- color: '#10b981',
361
- description: 'Tech Conference 2024\nDowntown Convention Center'
362
- }
363
- ];
364
- ```
365
-
366
- ## Timezone Handling
367
-
368
- SimpleCalendarJs relies on JavaScript's native Date object for timezone handling, which means events are automatically displayed in the **user's local timezone**.
369
-
370
- ### How It Works
371
-
372
- 1. **Automatic Conversion**: JavaScript Date objects automatically convert to the user's browser timezone
373
- 2. **No Configuration Needed**: The calendar has no timezone settings - it uses the browser's timezone
374
- 3. **Backend Responsibility**: Your backend should send timezone-aware date strings
375
-
376
- ### Best Practices
377
-
378
- **✓ Recommended - Send ISO 8601 strings with timezone:**
379
-
380
- ```javascript
381
- fetchEvents: async (start, end) => {
382
- const response = await fetch(`/api/events?start=${start}&end=${end}`);
383
- const events = await response.json();
384
-
385
- // Backend returns ISO 8601 strings with timezone info
386
- // Example: "2024-03-15T10:00:00Z" (UTC)
387
- // or: "2024-03-15T10:00:00-05:00" (EST)
388
-
389
- return events.map(event => ({
390
- ...event,
391
- start: new Date(event.start), // Automatically converts to local timezone
392
- end: new Date(event.end)
393
- }));
394
- }
395
- ```
396
-
397
- **✗ Avoid - Sending dates without timezone info:**
398
-
399
- ```javascript
400
- // BAD: "2024-03-15T10:00:00" (no timezone)
401
- // JavaScript interprets this as LOCAL time, not UTC
402
- // This can cause issues for users in different timezones
403
- ```
404
-
405
- ### Example: Multi-Timezone Scenario
406
-
407
- **Scenario**: Your server stores events in UTC, users are in different timezones
408
-
409
- ```javascript
410
- // Server returns (stored in UTC):
411
- {
412
- "title": "Team Meeting",
413
- "start": "2024-03-15T14:00:00Z", // 2:00 PM UTC
414
- "end": "2024-03-15T15:00:00Z" // 3:00 PM UTC
415
- }
416
-
417
- // User in New York (EST, UTC-5):
418
- // Calendar displays: 9:00 AM - 10:00 AM
419
-
420
- // User in London (GMT, UTC+0):
421
- // Calendar displays: 2:00 PM - 3:00 PM
422
-
423
- // User in Tokyo (JST, UTC+9):
424
- // Calendar displays: 11:00 PM - 12:00 AM
425
- ```
426
-
427
- ### Important Notes
428
-
429
- - **Storage**: Always store events in UTC in your database
430
- - **API Format**: Send dates as ISO 8601 strings with timezone information
431
- - **Display**: The calendar automatically shows events in the user's local timezone
432
- - **No Timezone Selector**: The calendar doesn't provide UI to change timezone - it always uses the browser's timezone
433
- - **Time Formatting**: Uses `Intl.DateTimeFormat` which respects the user's locale and timezone
434
-
435
- ### Example Backend Response
436
-
437
- ```json
438
- {
439
- "events": [
440
- {
441
- "id": 1,
442
- "title": "Global Team Standup",
443
- "start": "2024-03-15T14:00:00Z",
444
- "end": "2024-03-15T14:30:00Z",
445
- "description": "Daily standup - all timezones welcome"
446
- }
447
- ]
448
- }
449
- ```
450
-
451
- ## Tooltips
452
-
453
- SimpleCalendarJs includes a powerful tooltip system with **HTML support**, smart positioning, and extensive customization options.
454
-
455
- ### How Tooltips Work
456
-
457
- Tooltips appear when you hover over an event (default 400ms delay). They display content based on the following priority:
458
-
459
- 1. **`tooltip` property** (highest priority) - Custom tooltip content
460
- 2. **`description` property** - Falls back if no tooltip is provided
461
- 3. **`title` property** - Falls back if neither tooltip nor description is provided
462
-
463
- ### Basic Text Tooltips
464
-
465
- ```javascript
466
- const events = [
467
- {
468
- id: 1,
469
- title: 'Team Meeting',
470
- start: new Date('2024-03-15T10:00:00'),
471
- end: new Date('2024-03-15T11:00:00'),
472
- tooltip: 'Weekly team sync' // Simple text tooltip
473
- },
474
- {
475
- id: 2,
476
- title: 'Client Call',
477
- start: new Date('2024-03-15T14:00:00'),
478
- end: new Date('2024-03-15T15:00:00'),
479
- description: 'Requirements gathering session' // Used as tooltip
480
- }
481
- ];
482
- ```
483
-
484
- ### Rich HTML Tooltips
485
-
486
- **By default**, tooltips support HTML formatting with secure sanitization:
487
-
488
- ```javascript
489
- {
490
- id: 1,
491
- title: 'Design Review',
492
- start: new Date('2024-03-20T14:00:00'),
493
- end: new Date('2024-03-20T15:30:00'),
494
- tooltip: '<b>Product Design Review</b><br><br><i>New feature mockups:</i><br>• Dashboard v2.0<br>• Mobile redesign<br><br><a href="https://figma.com/example">View in Figma</a>'
495
- }
496
- ```
497
-
498
- **Supported HTML tags** (all sanitized for security):
499
- - `<b>`, `<strong>` - Bold text
500
- - `<i>`, `<em>` - Italic text
501
- - `<br>` - Line breaks
502
- - `<span style="color: #xxx;">` - Colored text (only `color` and `background-color` allowed)
503
- - `<a href="...">` - Links (automatically open in new tab, URL validated for safety)
504
-
505
- **Security features**:
506
- - Dangerous protocols blocked (`javascript:`, `data:`, `vbscript:`)
507
- - Only safe CSS properties allowed in `style` attributes
508
- - All event handlers stripped (`onclick`, etc.)
509
- - Script tags and other dangerous elements removed
510
-
511
- ### Multiline Tooltips
512
-
513
- Use `<br>` tags for HTML tooltips, or `\n` for plain text:
514
-
515
- ```javascript
516
- // HTML approach (recommended)
517
- {
518
- tooltip: '<b>Meeting Agenda:</b><br>• Introductions<br>• Timeline review<br>• Q&A session'
519
- }
520
-
521
- // Plain text approach
522
- {
523
- tooltipAllowHtml: false, // Disable HTML for this event
524
- tooltip: 'Meeting Agenda:\n• Introductions\n• Timeline review\n• Q&A session'
525
- }
526
- ```
527
-
528
- ### Configuration Options
529
-
530
- Control tooltip behavior globally:
531
-
532
- ```javascript
533
- const calendar = new SimpleCalendarJs('#calendar', {
534
- showTooltips: true, // Enable/disable tooltips (default: true)
535
- tooltipAllowHtml: true, // Allow HTML in tooltips (default: true)
536
- tooltipBgColor: '#1f2937', // Custom background color (default: null = CSS var)
537
- tooltipTextColor: '#f9fafb', // Custom text color (default: null = CSS var)
538
- tooltipMaxWidth: 250, // Maximum width in pixels (default: 250)
539
- tooltipDelay: 400, // Hover delay in milliseconds (default: 400)
540
- fetchEvents: async (start, end) => { ... }
541
- });
542
- ```
543
-
544
- ### Per-Event HTML Override
545
-
546
- Override the global `tooltipAllowHtml` setting for specific events:
547
-
548
- ```javascript
549
- {
550
- id: 1,
551
- title: 'Secure Event',
552
- tooltip: '<script>alert("xss")</script>', // This will be sanitized/removed
553
- tooltipAllowHtml: false // Force plain text for this event only
554
- }
555
- ```
556
-
557
- ### Smart Positioning
558
-
559
- Tooltips use **fixed positioning** and automatically adjust to stay visible:
560
-
561
- - **Fixed to viewport**: Never clipped by calendar container boundaries
562
- - **Edge detection**:
563
- - Near **top**: tooltip appears below the event
564
- - Near **right edge**: tooltip shifts left
565
- - Near **left edge**: tooltip shifts right
566
- - **Dynamic arrow**: Arrow always points to the hovered event center
567
-
568
- ### Styling Tooltips
569
-
570
- Customize via CSS variables:
571
-
572
- ```css
573
- :root {
574
- --cal-tooltip-bg: #1f2937; /* Background color */
575
- --cal-tooltip-text: #f9fafb; /* Text color */
576
- --cal-tooltip-border: #374151; /* Border color */
577
- --cal-tooltip-max-width: 250px; /* Maximum width */
578
- --cal-tooltip-padding: 8px 12px; /* Inner padding */
579
- --cal-tooltip-radius: 6px; /* Border radius */
580
- --cal-tooltip-font-size: 12px; /* Font size */
581
- --cal-tooltip-offset: 8px; /* Distance from event */
582
- --cal-tooltip-shadow: 0 4px 12px rgba(0, 0, 0, 0.15); /* Shadow */
583
- }
584
- ```
585
-
586
- Or use configuration options for colors:
587
-
588
- ```javascript
589
- const calendar = new SimpleCalendarJs('#calendar', {
590
- tooltipBgColor: '#dc2626', // Red background
591
- tooltipTextColor: '#ffffff', // White text
592
- });
593
- ```
594
-
595
- ### Disabling HTML
596
-
597
- To use only plain text tooltips globally:
598
-
599
- ```javascript
600
- const calendar = new SimpleCalendarJs('#calendar', {
601
- tooltipAllowHtml: false, // Disable HTML rendering
602
- });
603
- ```
604
-
605
- ### Advanced Example
606
-
607
- ```javascript
608
- {
609
- id: 1,
610
- title: 'Client Demo',
611
- start: new Date('2024-03-25T10:00:00'),
612
- end: new Date('2024-03-25T11:00:00'),
613
- tooltip: `
614
- <span style="color: #ef4444;"><b>⚡ HIGH PRIORITY</b></span><br><br>
615
- <b>Q4 Business Review</b><br>
616
- <i>Executive presentation</i><br><br>
617
- <b>Must prepare:</b><br>
618
- • Financial projections<br>
619
- • ROI analysis<br>
620
- • Growth charts<br><br>
621
- <a href="https://example.com/deck">View presentation deck</a>
622
- `,
623
- tooltipAllowHtml: true
624
- }
625
- ```
626
- - Special characters are automatically escaped for security
627
- - Maximum width is 250px by default (can be customized via CSS variables)
628
-
629
- ## Drag and Drop
630
-
631
- SimpleCalendarJs supports drag and drop for moving events and resizing them to change their duration.
632
-
633
- ### Configuration
634
-
635
- ```javascript
636
- const calendar = new SimpleCalendarJs('#calendar', {
637
- enableDragDrop: true, // Enable moving events
638
- enableResize: true, // Enable resizing events
639
-
640
- onEventDrop: (event, oldStart, oldEnd, newStart, newEnd) => {
641
- // Detect if this is a move or resize
642
- const isMoved = oldStart.getTime() !== newStart.getTime();
643
- const isResized = oldEnd.getTime() !== newEnd.getTime() && !isMoved;
644
-
645
- if (isMoved) {
646
- console.log(`Event "${event.title}" moved to ${newStart}`);
647
- } else if (isResized) {
648
- console.log(`Event "${event.title}" resized to end at ${newEnd}`);
649
- }
650
-
651
- // Update your backend
652
- await fetch(`/api/events/${event.id}`, {
653
- method: 'PATCH',
654
- headers: { 'Content-Type': 'application/json' },
655
- body: JSON.stringify({
656
- start: newStart.toISOString(),
657
- end: newEnd.toISOString(),
658
- allDay: event.allDay
659
- })
660
- });
661
- }
662
- });
663
- ```
664
-
665
- ### Moving Events (`enableDragDrop`)
666
-
667
- **How It Works:**
668
- - **Drag Initiation**: Click and hold on an event, then move at least 5px or wait 150ms
669
- - **Visual Feedback**: The event follows your cursor while dragging
670
- - **Snap to Grid**: Events snap to 15-minute intervals in week/day views
671
- - **Drop**: Release to drop the event at the new date/time
672
- - **Cancel**: Press ESC to cancel the drag operation
673
- - **Duration Preservation**: Events maintain their duration when moved
674
- - **Touch Support**: Full support for mobile/tablet touch gestures
675
-
676
- **Cross-Boundary Conversion:**
677
-
678
- When dragging events between different sections:
679
-
680
- **Timed Event → All-Day Section (Week/Day Views):**
681
- - Converts to an all-day event
682
- - Preserves the day span
683
-
684
- **All-Day Event → Timed Section (Week/Day Views):**
685
- - Converts to a timed event
686
- - Default duration: 1 hour
687
- - Snaps to the time slot where dropped
688
-
689
- **Month View:**
690
- - Events maintain their original type (all-day stays all-day, timed stays timed)
691
- - Timed events preserve their original time of day
692
-
693
- ### Resizing Events (`enableResize`)
694
-
695
- **Horizontal Resize (All-Day Events):**
696
- - **Visual Indicator**: Small vertical line appears on the right edge when hovering
697
- - **How It Works**: Drag the right edge to change the number of days the event spans
698
- - **Available In**: Month view, week/day all-day sections
699
- - **Minimum**: 1 day
700
-
701
- **Vertical Resize (Timed Events):**
702
- - **Visual Indicator**: Small horizontal line appears at the bottom when hovering
703
- - **How It Works**: Drag the bottom edge to change the end time
704
- - **Available In**: Week/day timed sections
705
- - **Snap to Grid**: 15-minute intervals
706
- - **Minimum**: 15 minutes
707
- - **Live Feedback**: Time display updates to show start and end times as you drag
708
-
709
- ### Callback Parameters
710
-
711
- The `onEventDrop` callback receives the same parameters for both move and resize operations:
712
-
713
- | Parameter | Type | Description |
714
- |-----------|------|-------------|
715
- | `event` | Object | The updated event object (with new start/end) |
716
- | `oldStart` | Date | Original start date/time |
717
- | `oldEnd` | Date | Original end date/time |
718
- | `newStart` | Date | New start date/time |
719
- | `newEnd` | Date | New end date/time |
720
-
721
- **Detecting Operation Type:**
722
- - **Move**: `oldStart !== newStart`
723
- - **Resize**: `oldStart === newStart` and `oldEnd !== newEnd`
724
-
725
- ### Important Notes
726
-
727
- - The calendar updates the event internally before firing the callback
728
- - You **must** update your backend in the `onEventDrop` callback
729
- - If the backend update fails, call `calendar.refresh()` to revert to the previous state
730
- - Both features are disabled in list view (read-only)
731
- - You can enable one, both, or neither feature independently
732
-
733
- ## Month View Timed Event Display Style
734
-
735
- The `monthTimedEventStyle` option controls how timed events are displayed in month view:
736
-
737
- ### List Style (Default: `'list'`)
738
- Schedule-style display with horizontal layout:
739
- - **Colored dot**: Shows event color as a small circle
740
- - **Time**: Displays start time (if `showTimeInItems` is enabled)
741
- - **Title**: Event title truncated with ellipsis if too long
742
- - **Compact**: Clean, minimal appearance similar to schedule apps
743
-
744
- ```javascript
745
- const calendar = new SimpleCalendarJs('#calendar', {
746
- monthTimedEventStyle: 'list', // Default
747
- showTimeInItems: true // Shows time next to dot
748
- });
749
- ```
750
-
751
- ### Block Style (`'block'`)
752
- Traditional calendar block display:
753
- - **Colored Background**: Full event background in event color
754
- - **Time Display**: Start time shown inside block (if enabled)
755
- - **Classic Look**: Traditional calendar appearance
756
-
757
- ```javascript
758
- const calendar = new SimpleCalendarJs('#calendar', {
759
- monthTimedEventStyle: 'block'
760
- });
761
- ```
762
-
763
- **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.
764
-
765
- ## Month View Row Heights
766
-
767
- Month view automatically adapts row heights based on your container's height and event content:
768
-
769
- ### Fixed Height Container
770
- When you set an explicit height on the calendar container (e.g., `height: 600px`), the calendar calculates the maximum number of events that can fit in each cell and displays a "+N more" indicator for overflow events.
771
-
772
- ```javascript
773
- // HTML with fixed height
774
- <div id="calendar" style="height: 600px;"></div>
775
-
776
- // Calendar automatically limits events to fit the available space
777
- const calendar = new SimpleCalendarJs('#calendar', {
778
- defaultView: 'month'
779
- });
780
- ```
781
-
782
- ### Natural Height Container (Auto/Unlimited Mode)
783
- When the container has no explicit height (or `height: auto`), the calendar displays **all events** with **variable row heights**:
784
-
785
- - **Rows with many events** → Automatically expand to fit all events
786
- - **Rows with few events** → Use minimal vertical space
787
- - **Empty rows** → Collapse to minimum height (80px)
788
- - **Each row is independent** → Heights calculated individually per row
789
-
790
- ```javascript
791
- // HTML with natural height
792
- <div id="calendar" style="height: auto;"></div>
793
-
794
- // or
795
- <div id="calendar"></div>
796
-
797
- // All events displayed, rows adapt to content
798
- const calendar = new SimpleCalendarJs('#calendar', {
799
- defaultView: 'month'
800
- });
801
- ```
802
-
803
- ### Benefits of Variable Row Heights
804
- - **Eliminates wasted space**: Empty rows don't take up unnecessary vertical space
805
- - **Optimal density**: Each week row is exactly as tall as it needs to be
806
- - **No uniform padding**: Weeks with 20 events don't force empty weeks to be equally tall
807
- - **Better UX**: Users see all events without clicking "+N more" when there's room
808
-
809
- **Recommendation**: Use fixed height containers when you need predictable layouts, and natural height when you want to show all events with minimal scrolling.
810
-
811
- ## Auto-Contrast Text Color
812
-
813
- SimpleCalendarJs can automatically calculate the best text color for event text based on the background color, ensuring optimal readability with configurable contrast levels.
814
-
815
- ### Contrast Levels
816
-
817
- Choose from three contrast levels to match your design preferences:
818
-
819
- #### High Contrast (default: `'high'`)
820
- Pure black or white text for maximum readability:
821
- - **Light backgrounds** (luminance > 0.5) → Black text (#000000)
822
- - **Dark backgrounds** (luminance ≤ 0.5) → White text (#ffffff)
823
-
824
- #### Medium Contrast (`'medium'`)
825
- Darker or lighter shade of the background color for a more integrated look:
826
- - **Light backgrounds** → Darkened version of the background color
827
- - **Dark backgrounds** → Lightened version of the background color
828
-
829
- #### Low Contrast (`'low'`)
830
- Subtle variation that maintains the color family:
831
- - **Light backgrounds** → Slightly darker shade
832
- - **Dark backgrounds** → Slightly lighter shade
833
-
834
- ### Basic Usage
835
-
836
- ```javascript
837
- const calendar = new SimpleCalendarJs('#calendar', {
838
- autoContrastText: true, // Enable automatic text color calculation
839
- contrastLevel: 'medium', // Choose contrast level: 'high', 'medium', or 'low'
840
- fetchEvents: async (start, end) => {
841
- return [
842
- {
843
- id: 1,
844
- title: 'Team Meeting',
845
- start: new Date('2024-03-15T10:00:00'),
846
- color: '#ffeb3b' // Yellow background → auto-calculated dark yellow text
847
- },
848
- {
849
- id: 2,
850
- title: 'Project Review',
851
- start: new Date('2024-03-16T14:00:00'),
852
- color: '#1a237e' // Dark blue background → auto-calculated light blue text
853
- }
854
- ];
855
- }
856
- });
857
- ```
858
-
859
- ### Comparison of Contrast Levels
860
-
861
- ```javascript
862
- // High contrast (default) - Black or white
863
- { color: '#4f46e5', autoContrastText: true, contrastLevel: 'high' }
864
- // Result: White text (#ffffff)
865
-
866
- // Medium contrast - Darker/lighter shade
867
- { color: '#4f46e5', autoContrastText: true, contrastLevel: 'medium' }
868
- // Result: Light indigo text (calculated from background)
869
-
870
- // Low contrast - Subtle variation
871
- { color: '#4f46e5', autoContrastText: true, contrastLevel: 'low' }
872
- // Result: Slightly lighter indigo text
873
- ```
874
-
875
- ### Manual Override
876
-
877
- Individual events can override the auto-calculated color with a custom `textColor`:
878
-
879
- ```javascript
880
- {
881
- id: 1,
882
- title: 'Special Event',
883
- start: new Date('2024-03-15T10:00:00'),
884
- color: '#4caf50', // Green background
885
- textColor: '#ffeb3b' // Force yellow text (overrides auto-contrast)
886
- }
887
- ```
888
-
889
- ### Priority Order
890
-
891
- Text color is determined in this order:
892
- 1. **`event.textColor`** (highest priority) - Manual override
893
- 2. **`autoContrastText: true`** - Automatic calculation based on background color
894
- 3. **CSS variable** `--cal-event-text` (default) - Fallback to theme default
895
-
896
- ### When to Use
897
-
898
- **Enable `autoContrastText` when:**
899
- - Events have diverse background colors from user preferences or categories
900
- - You want consistent readability without manually setting text colors
901
- - Event colors are generated dynamically
902
-
903
- **Disable `autoContrastText` (default) when:**
904
- - All events use similar colors with known good contrast
905
- - You prefer full control over text colors via CSS or event properties
906
- - You want to maintain backward compatibility
907
-
908
- ### Browser Support
909
-
910
- The auto-contrast calculation works in all modern browsers and has no dependencies. It handles:
911
- - Hex colors with or without `#` prefix (#ff0000 or ff0000)
912
- - CSS variables (falls back to theme default)
913
- - Invalid colors (falls back to theme default)
914
-
915
- ## HTML in Event Titles
916
-
917
- SimpleCalendarJs allows basic HTML formatting in event titles for icons, bold/italic text, and simple styling while maintaining security through automatic sanitization.
918
-
919
- ### Security & Sanitization
920
-
921
- All HTML is automatically sanitized to prevent XSS attacks:
922
- - **Allowed tags**: `<b>`, `<i>`, `<strong>`, `<em>`, `<span>`, `<br>`
923
- - **Allowed attributes**: `class` (for icon libraries like Font Awesome)
924
- - **Blocked**: `<script>`, event handlers (`onclick`, `onerror`), dangerous attributes
925
- - **Character limit**: Class names are sanitized to alphanumeric, dash, underscore, and space only
926
-
927
- ### Basic Usage
928
-
929
- Enable HTML globally for all events:
930
-
931
- ```javascript
932
- const calendar = new SimpleCalendarJs('#calendar', {
933
- allowHtmlInEvents: true, // Enable HTML rendering
934
- fetchEvents: async (start, end) => {
935
- return [
936
- {
937
- id: 1,
938
- title: '<b>Important</b> Meeting',
939
- start: new Date('2024-03-15T10:00:00')
940
- },
941
- {
942
- id: 2,
943
- title: 'Design <i>Review</i>',
944
- start: new Date('2024-03-16T14:00:00')
945
- }
946
- ];
947
- }
948
- });
949
- ```
950
-
951
- ### Font Awesome Icons
952
-
953
- Perfect for adding visual indicators:
954
-
955
- ```javascript
956
- {
957
- id: 1,
958
- title: '<i class="fas fa-video"></i> Video Call',
959
- start: new Date('2024-03-15T10:00:00')
960
- }
961
-
962
- {
963
- id: 2,
964
- title: '<i class="fas fa-plane"></i> Flight to NYC',
965
- start: new Date('2024-03-16T14:00:00'),
966
- allDay: true
967
- }
968
-
969
- {
970
- id: 3,
971
- title: '<i class="fas fa-birthday-cake"></i> <b>Sarah</b> Birthday',
972
- start: new Date('2024-03-20T00:00:00'),
973
- allDay: true
974
- }
975
- ```
976
-
977
- ### Other Icon Libraries
978
-
979
- Works with any icon library that uses class names:
980
-
981
- ```javascript
982
- // Material Icons
983
- { title: '<i class="material-icons">event</i> Meeting' }
984
-
985
- // Bootstrap Icons
986
- { title: '<i class="bi bi-calendar"></i> Appointment' }
987
-
988
- // Ionicons
989
- { title: '<i class="ion ion-md-call"></i> Phone Call' }
990
- ```
991
-
992
- ### Per-Event Control
993
-
994
- Override the global setting for specific events:
995
-
996
- ```javascript
997
- const calendar = new SimpleCalendarJs('#calendar', {
998
- allowHtmlInEvents: false, // Disabled globally
999
- fetchEvents: async (start, end) => {
1000
- return [
1001
- {
1002
- id: 1,
1003
- title: 'Regular Meeting', // Plain text
1004
- start: new Date('2024-03-15T10:00:00')
1005
- },
1006
- {
1007
- id: 2,
1008
- title: '<i class="fas fa-star"></i> <b>VIP</b> Client Call',
1009
- start: new Date('2024-03-16T14:00:00'),
1010
- allowHtml: true // Enable HTML only for this event
1011
- }
1012
- ];
1013
- }
1014
- });
1015
- ```
1016
-
1017
- ### Supported HTML Tags
1018
-
1019
- | Tag | Purpose | Example |
1020
- |-----|---------|---------|
1021
- | `<b>`, `<strong>` | Bold text | `<b>Important</b>` |
1022
- | `<i>`, `<em>` | Italic text or icons | `<i class="fas fa-star"></i>` |
1023
- | `<span>` | Inline styling container | `<span class="custom-class">Text</span>` |
1024
- | `<br>` | Line break | `Line 1<br>Line 2` |
1025
-
1026
- ### Important Notes
1027
-
1028
- - **Default behavior**: HTML is **enabled by default** (`allowHtmlInEvents: true`, `tooltipAllowHtml: true`)
1029
- - **Security first**: All HTML is sanitized - dangerous tags and attributes are stripped
1030
- - **Event titles**: Only `class` attribute is preserved (for icons). `style` attributes are removed
1031
- - **Tooltips**: Support both `class` and safe `style` attributes (only `color` and `background-color` properties allowed)
1032
- - **Plain text fallback**: If `allowHtmlInEvents`/`tooltipAllowHtml` is false, HTML is escaped and displayed as text
1033
-
1034
- ### Best Practices
1035
-
1036
- **✓ Recommended**:
1037
- - Use for icons and simple formatting
1038
- - Keep HTML minimal and semantic
1039
- - Test with `allowHtmlInEvents: false` to ensure graceful degradation
1040
-
1041
- **✗ Avoid**:
1042
- - Complex nested HTML structures
1043
- - Custom styles via `style` attribute (use `class` and external CSS)
1044
- - User-generated HTML without validation
217
+ ## Configuration Options
1045
218
 
1046
- ### Example: Icon + Formatting
219
+ Here are some of the most commonly used configuration options:
1047
220
 
1048
221
  ```javascript
1049
- const events = [
1050
- {
1051
- id: 1,
1052
- title: '<i class="fas fa-users"></i> <b>Team Standup</b>',
1053
- start: new Date('2024-03-15T09:00:00'),
1054
- color: '#3b82f6'
1055
- },
1056
- {
1057
- id: 2,
1058
- title: '<i class="fas fa-exclamation-triangle"></i> <b>Urgent</b>: Server Down',
1059
- start: new Date('2024-03-15T14:30:00'),
1060
- color: '#ef4444'
1061
- }
1062
- ];
1063
-
1064
222
  const calendar = new SimpleCalendarJs('#calendar', {
1065
- allowHtmlInEvents: true,
1066
- fetchEvents: async () => events
223
+ // View Settings
224
+ defaultView: 'month', // 'month', 'week', 'day', 'list'
225
+
226
+ // Picker Modes
227
+ mode: 'calendar', // 'calendar', 'date-picker', 'range-picker'
228
+
229
+ // Internationalization
230
+ locale: 'en-US', // Any valid locale (e.g., 'es-ES', 'fr-FR', 'ja-JP')
231
+ firstDayOfWeek: 0, // 0 = Sunday, 1 = Monday
232
+
233
+ // Events
234
+ fetchEvents: async (start, end) => { /* ... */ },
235
+ events: [], // Static events array
236
+
237
+ // Interactions
238
+ enableDragDrop: false, // Enable drag & drop
239
+ enableResize: false, // Enable event resizing
240
+
241
+ // Callbacks
242
+ onEventClick: (event, mouseEvent) => {},
243
+ onDateClick: (date, mouseEvent) => {},
244
+ onEventDrop: (event, newStart, newEnd) => {},
245
+ onEventResize: (event, newStart, newEnd) => {},
246
+ onDateSelect: (date) => {}, // For date-picker mode
247
+ onRangeSelect: (start, end) => {}, // For range-picker mode
248
+
249
+ // Styling
250
+ darkMode: 'auto', // 'auto', 'light', 'dark'
251
+ enableHtmlInTitles: true, // Allow HTML in event titles
252
+
253
+ // Advanced
254
+ timezone: 'local', // 'local', 'UTC', or IANA timezone
255
+ monthViewTimedEventStyle: 'list', // 'list' or 'block'
1067
256
  });
1068
257
  ```
1069
258
 
1070
259
  ## API Methods
1071
260
 
1072
- ### General Methods
1073
-
1074
- ```javascript
1075
- // Switch view
1076
- calendar.setView('week');
1077
-
1078
- // Navigate forward/backward
1079
- calendar.navigate(1); // Next period
1080
- calendar.navigate(-1); // Previous period
1081
-
1082
- // Jump to specific date
1083
- calendar.goToDate(new Date('2024-12-25'));
1084
-
1085
- // Jump to today
1086
- calendar.goToToday();
1087
-
1088
- // Refresh calendar (clear cache and re-fetch events)
1089
- calendar.refresh();
1090
-
1091
- // Cleanup (important for SPAs)
1092
- calendar.destroy();
1093
- ```
1094
-
1095
- ### Picker Mode Methods
1096
-
1097
- Available when using `mode: 'date-picker'` or `mode: 'range-picker'`:
1098
-
1099
261
  ```javascript
1100
- // Get selected date (date-picker mode)
1101
- const selectedDate = calendar.getSelectedDate(); // Returns Date or null
262
+ // Navigation
263
+ calendar.goToDate(new Date());
264
+ calendar.next();
265
+ calendar.prev();
266
+ calendar.today();
1102
267
 
1103
- // Set selected date (date-picker mode)
1104
- calendar.setSelectedDate(new Date('2024-12-25'));
268
+ // View Management
269
+ calendar.changeView('week');
270
+ calendar.getCurrentView();
1105
271
 
1106
- // Get selected range (range-picker mode)
1107
- const range = calendar.getSelectedRange(); // Returns {start: Date, end: Date}
272
+ // Events
273
+ calendar.addEvent(event);
274
+ calendar.updateEvent(eventId, updates);
275
+ calendar.deleteEvent(eventId);
276
+ calendar.refetchEvents();
1108
277
 
1109
- // Set selected range (range-picker mode)
1110
- calendar.setSelectedRange(
1111
- new Date('2024-12-01'), // start date
1112
- new Date('2024-12-07') // end date
1113
- );
1114
-
1115
- // Clear all selections (both picker modes)
278
+ // Picker Mode (date-picker/range-picker)
279
+ calendar.setSelectedDate(date);
280
+ calendar.getSelectedDate();
281
+ calendar.setSelectedRange(startDate, endDate);
282
+ calendar.getSelectedRange();
1116
283
  calendar.clearSelection();
1117
- ```
1118
-
1119
- ### Event Caching
1120
-
1121
- SimpleCalendarJs intelligently caches fetched events to minimize unnecessary network requests:
1122
-
1123
- - **Smart fetch strategy**: Always fetches the **full month grid** (including leading/trailing days), regardless of current view
1124
- - **Automatic caching**: Events are cached by date range after each fetch
1125
- - **Efficient reuse**: View switches and navigation within the cached range use cached data (no spinner, instant display)
1126
- - **Fetch only when needed**: New data is fetched only when the required range extends beyond the cache
1127
- - **Manual refresh**: Call `calendar.refresh()` when events change externally (added, updated, or deleted)
1128
-
1129
- **How it works:**
1130
- - When in **month/week/day views**: Fetches the full month grid for the current date context
1131
- - When in **list view**: Fetches the specific forward range (configurable with `listDaysForward`)
1132
- - Subsequent navigation within the same month uses cached data
1133
- - Navigation to a different month triggers a new fetch for that month's full grid
1134
-
1135
- **Example behavior:**
1136
- ```javascript
1137
- // Month view Feb 2026: Fetches Jan 28 - Mar 1 (full month grid)
1138
- calendar.setView('month'); // ✓ Fetch: Jan 28 - Mar 1
1139
-
1140
- // Switch to week view (Feb 20-26): Already in cache, no fetch
1141
- calendar.setView('week'); // ✗ No fetch (instant)
1142
-
1143
- // Switch to day view (Feb 22): Already in cache, no fetch
1144
- calendar.setView('day'); // ✗ No fetch (instant)
1145
-
1146
- // Navigate next week (Feb 27 - Mar 5): Extends beyond cache
1147
- // Fetches March's full month grid (Mar 1 - Apr 5)
1148
- calendar.navigate(1); // ✓ Fetch: Mar 1 - Apr 5
1149
-
1150
- // Navigate next week (Mar 6-12): Already in March's cache
1151
- calendar.navigate(1); // ✗ No fetch (instant)
1152
-
1153
- // Navigate previous week (Feb 27 - Mar 5): Partially outside cache
1154
- // Fetches February's full month grid (Jan 28 - Mar 1)
1155
- calendar.navigate(-1); // ✓ Fetch: Jan 28 - Mar 1
1156
-
1157
- // After external event changes, refresh to clear cache
1158
- addEventToDatabase(newEvent);
1159
- calendar.refresh(); // ✓ Fetch (clears cache)
1160
- ```
1161
-
1162
- ## Persisting User Preferences
1163
-
1164
- 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:
1165
-
1166
- ### Using LocalStorage (Persistent Across Sessions)
1167
-
1168
- ```javascript
1169
- // Restore saved preferences or use defaults
1170
- const savedView = localStorage.getItem('calendarView') || 'month';
1171
- const savedDate = localStorage.getItem('calendarDate');
1172
-
1173
- const calendar = new SimpleCalendarJs('#calendar', {
1174
- defaultView: savedView,
1175
- defaultDate: savedDate ? new Date(savedDate) : null,
1176
-
1177
- // Save view changes
1178
- onViewChange: (view) => {
1179
- localStorage.setItem('calendarView', view);
1180
- },
1181
-
1182
- // Save navigation (current date)
1183
- onNavigate: (startDate) => {
1184
- localStorage.setItem('calendarDate', startDate.toISOString());
1185
- }
1186
- });
1187
- ```
1188
-
1189
- ### Using SessionStorage (Per-Tab)
1190
-
1191
- ```javascript
1192
- // Same as localStorage but survives only for the current tab/session
1193
- const calendar = new SimpleCalendarJs('#calendar', {
1194
- defaultView: sessionStorage.getItem('calendarView') || 'month',
1195
- onViewChange: (view) => sessionStorage.setItem('calendarView', view),
1196
- onNavigate: (start) => sessionStorage.setItem('calendarDate', start.toISOString())
1197
- });
1198
- ```
1199
-
1200
- ### Using URL Query Parameters (Shareable State)
1201
-
1202
- ```javascript
1203
- // Read from URL
1204
- const params = new URLSearchParams(window.location.search);
1205
- const view = params.get('view') || 'month';
1206
- const date = params.get('date') ? new Date(params.get('date')) : null;
1207
-
1208
- const calendar = new SimpleCalendarJs('#calendar', {
1209
- defaultView: view,
1210
- defaultDate: date,
1211
-
1212
- onViewChange: (newView) => {
1213
- const url = new URL(window.location);
1214
- url.searchParams.set('view', newView);
1215
- window.history.pushState({}, '', url);
1216
- },
1217
-
1218
- onNavigate: (startDate) => {
1219
- const url = new URL(window.location);
1220
- url.searchParams.set('date', startDate.toISOString().split('T')[0]);
1221
- window.history.pushState({}, '', url);
1222
- }
1223
- });
1224
- ```
1225
-
1226
- ### React Example with State Hook
1227
-
1228
- ```javascript
1229
- function MyCalendar() {
1230
- const [view, setView] = useState(
1231
- () => localStorage.getItem('calendarView') || 'month'
1232
- );
1233
-
1234
- const [currentDate, setCurrentDate] = useState(
1235
- () => {
1236
- const saved = localStorage.getItem('calendarDate');
1237
- return saved ? new Date(saved) : new Date();
1238
- }
1239
- );
1240
-
1241
- const handleViewChange = (newView) => {
1242
- setView(newView);
1243
- localStorage.setItem('calendarView', newView);
1244
- };
1245
-
1246
- const handleNavigate = (startDate) => {
1247
- setCurrentDate(startDate);
1248
- localStorage.setItem('calendarDate', startDate.toISOString());
1249
- };
1250
-
1251
- return (
1252
- <SimpleCalendarJsReact
1253
- defaultView={view}
1254
- defaultDate={currentDate}
1255
- onViewChange={handleViewChange}
1256
- onNavigate={handleNavigate}
1257
- fetchEvents={fetchEvents}
1258
- />
1259
- );
1260
- }
1261
- ```
1262
-
1263
- ### Vue Example with Composable
1264
-
1265
- ```javascript
1266
- // composables/useCalendarPreferences.js
1267
- import { ref, watch } from 'vue';
1268
-
1269
- export function useCalendarPreferences() {
1270
- const view = ref(localStorage.getItem('calendarView') || 'month');
1271
- const currentDate = ref(
1272
- localStorage.getItem('calendarDate')
1273
- ? new Date(localStorage.getItem('calendarDate'))
1274
- : new Date()
1275
- );
1276
-
1277
- watch(view, (newView) => {
1278
- localStorage.setItem('calendarView', newView);
1279
- });
1280
-
1281
- watch(currentDate, (newDate) => {
1282
- localStorage.setItem('calendarDate', newDate.toISOString());
1283
- });
1284
-
1285
- return { view, currentDate };
1286
- }
1287
-
1288
- // In component
1289
- <script setup>
1290
- import { useCalendarPreferences } from './composables/useCalendarPreferences';
1291
- const { view, currentDate } = useCalendarPreferences();
1292
-
1293
- const handleViewChange = (newView) => {
1294
- view.value = newView;
1295
- };
1296
-
1297
- const handleNavigate = (startDate) => {
1298
- currentDate.value = startDate;
1299
- };
1300
- </script>
1301
-
1302
- <template>
1303
- <SimpleCalendarJsVue
1304
- :defaultView="view"
1305
- :defaultDate="currentDate"
1306
- @viewChange="handleViewChange"
1307
- @navigate="handleNavigate"
1308
- />
1309
- </template>
1310
- ```
1311
-
1312
- ### Server-Side Preferences (Synced Across Devices)
1313
-
1314
- ```javascript
1315
- // Load preferences from your API
1316
- const preferences = await fetch('/api/user/calendar-preferences').then(r => r.json());
1317
-
1318
- const calendar = new SimpleCalendarJs('#calendar', {
1319
- defaultView: preferences.view || 'month',
1320
- defaultDate: preferences.lastViewedDate ? new Date(preferences.lastViewedDate) : null,
1321
-
1322
- onViewChange: async (view) => {
1323
- // Debounce to avoid too many API calls
1324
- clearTimeout(window.savePreferencesTimeout);
1325
- window.savePreferencesTimeout = setTimeout(() => {
1326
- fetch('/api/user/calendar-preferences', {
1327
- method: 'PATCH',
1328
- headers: { 'Content-Type': 'application/json' },
1329
- body: JSON.stringify({ view })
1330
- });
1331
- }, 1000);
1332
- }
1333
- });
1334
- ```
1335
-
1336
- ### What to Persist
1337
-
1338
- Common preferences to save:
1339
- - **View mode**: `'month'`, `'week'`, `'day'`, or `'list'`
1340
- - **Current date**: Last viewed date for returning to the same position
1341
- - **Enabled views**: Which view modes the user prefers to have available
1342
- - **UI preferences**: Dark mode, show/hide options
1343
-
1344
- **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.
1345
-
1346
- ## Internationalization
1347
-
1348
- SimpleCalendarJs uses the native Intl.DateTimeFormat API for full locale support. Simply pass a valid locale code:
1349
-
1350
- ```javascript
1351
- // French
1352
- const calendar = new SimpleCalendarJs('#calendar', {
1353
- locale: 'fr-FR',
1354
- weekStartsOn: 1 // Monday
1355
- });
1356
-
1357
- // Japanese
1358
- const calendar = new SimpleCalendarJs('#calendar', {
1359
- locale: 'ja-JP'
1360
- });
1361
-
1362
- // Arabic (RTL support automatic)
1363
- const calendar = new SimpleCalendarJs('#calendar', {
1364
- locale: 'ar-SA'
1365
- });
1366
- ```
1367
-
1368
- 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).
1369
-
1370
- ### Weekday Name Format
1371
-
1372
- Control how day names are displayed using the `weekdayFormat` option:
1373
-
1374
- ```javascript
1375
- const calendar = new SimpleCalendarJs('#calendar', {
1376
- locale: 'pt-PT',
1377
- weekdayFormat: 'long' // Full day names
1378
- });
1379
- ```
1380
-
1381
- **Available formats:**
1382
- - `'narrow'` - 1-2 letter abbreviation (S, M, T, W, T, F, S)
1383
- - `'short'` - Short abbreviation (Sun, Mon, Tue... or Dom, Seg, Ter...)
1384
- - `'long'` - Full day name (Sunday, Monday... or Domingo, Segunda-feira...)
1385
-
1386
- **Examples:**
1387
-
1388
- ```javascript
1389
- // English with narrow format
1390
- new SimpleCalendarJs('#calendar', {
1391
- locale: 'en-US',
1392
- weekdayFormat: 'narrow' // S M T W T F S
1393
- });
1394
-
1395
- // Portuguese (Portugal) with long format
1396
- new SimpleCalendarJs('#calendar', {
1397
- locale: 'pt-PT',
1398
- weekdayFormat: 'long' // Domingo Segunda-feira Terça-feira...
1399
- });
1400
-
1401
- // Portuguese (Brazil) with short format (default)
1402
- new SimpleCalendarJs('#calendar', {
1403
- locale: 'pt-BR',
1404
- weekdayFormat: 'short' // Dom Seg Ter Qua...
1405
- });
1406
- ```
1407
-
1408
- **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.
1409
-
1410
- ### List View
1411
-
1412
- Display upcoming events in a chronological list format. Perfect for mobile devices and quick overview of upcoming events.
1413
-
1414
- ```javascript
1415
- const calendar = new SimpleCalendarJs('#calendar', {
1416
- defaultView: 'list', // Start in list view
1417
- enabledViews: ['month', 'week', 'day', 'list'], // Include list in views
1418
- listDaysForward: 30 // Show next 30 days (default)
1419
- });
1420
- ```
1421
-
1422
- **Features:**
1423
- - Shows events from current date/time forward
1424
- - Groups events by date with sticky headers
1425
- - Color-coded event indicators
1426
- - Mobile-friendly design
1427
- - Configurable range with `listDaysForward` option
1428
-
1429
- **Example:**
1430
-
1431
- ```javascript
1432
- // Show next 90 days in list view
1433
- new SimpleCalendarJs('#calendar', {
1434
- defaultView: 'list',
1435
- enabledViews: ['month', 'list'], // Only month and list views
1436
- listDaysForward: 90,
1437
- fetchEvents: async (start, end) => {
1438
- // Your event fetching logic
1439
- }
1440
- });
1441
- ```
1442
-
1443
- ## Dark Mode
1444
-
1445
- Dark mode is automatically detected from the user's system preferences. You can also manually toggle it:
1446
-
1447
- ```javascript
1448
- // Set data-theme attribute on <html> or <body>
1449
- document.documentElement.setAttribute('data-theme', 'dark');
1450
-
1451
- // Or add a CSS class
1452
- document.documentElement.classList.add('dark');
1453
- ```
1454
-
1455
- For framework wrappers, use the `darkMode` prop:
1456
-
1457
- ```jsx
1458
- // React
1459
- <SimpleCalendarJsReact darkMode={isDark} />
1460
-
1461
- // Vue
1462
- <SimpleCalendarJsVue :darkMode="isDark" />
1463
-
1464
- // Angular
1465
- <simple-calendar-js [darkMode]="isDark"></simple-calendar-js>
1466
- ```
1467
-
1468
- ## Styling & Theming
1469
-
1470
- Customize the calendar appearance using CSS custom properties.
1471
-
1472
- ### Override Light Mode Colors
1473
-
1474
- Target the `.uc-calendar` class to customize light mode:
1475
-
1476
- ```css
1477
- .uc-calendar {
1478
- /* Primary color */
1479
- --cal-primary: #10b981; /* Green theme */
1480
- --cal-primary-dark: #059669;
1481
- --cal-primary-light: #d1fae5;
1482
-
1483
- /* Today indicator */
1484
- --cal-today-bg: #d1fae5;
1485
- --cal-today-text: #059669;
1486
- }
1487
- ```
1488
-
1489
- ### Override Dark Mode Colors
1490
-
1491
- Target `.uc-calendar.uc-dark` to customize dark mode:
1492
-
1493
- ```css
1494
- .uc-calendar.uc-dark {
1495
- /* Dark mode colors */
1496
- --cal-bg: #1f2937;
1497
- --cal-bg-secondary: #111827;
1498
- --cal-text: #f9fafb;
1499
- --cal-border: #374151;
1500
-
1501
- /* Dark mode today/selected indicators */
1502
- --cal-today-bg: #065f46; /* Custom dark green */
1503
- --cal-selected-bg: #047857;
1504
- }
1505
- ```
1506
-
1507
- ### Customize Both Light and Dark Modes
1508
-
1509
- Example with a complete brand color scheme:
1510
-
1511
- ```css
1512
- /* Light mode - Yellow theme */
1513
- .uc-calendar {
1514
- --cal-primary: #eab308; /* yellow-500 */
1515
- --cal-primary-dark: #ca8a04; /* yellow-600 */
1516
- --cal-primary-light: #fef9c3; /* yellow-100 */
1517
- --cal-today-bg: #fef9c3;
1518
- --cal-today-text: #854d0e;
1519
- }
1520
284
 
1521
- /* Dark mode - Yellow theme */
1522
- .uc-calendar.uc-dark {
1523
- --cal-primary: #eab308;
1524
- --cal-primary-dark: #facc15;
1525
- --cal-primary-light: #713f12;
1526
- --cal-today-bg: #713f12; /* yellow-900 */
1527
- --cal-selected-bg: #854d0e; /* yellow-800 */
1528
- }
285
+ // Cleanup
286
+ calendar.destroy();
1529
287
  ```
1530
288
 
1531
- ### Available CSS Variables
1532
-
1533
- **Colors:**
1534
- - `--cal-primary`, `--cal-primary-dark`, `--cal-primary-light` - Primary theme colors
1535
- - `--cal-bg`, `--cal-bg-secondary` - Background colors
1536
- - `--cal-text`, `--cal-text-subtle`, `--cal-text-muted` - Text colors
1537
- - `--cal-border`, `--cal-border-strong` - Border colors
1538
- - `--cal-today-bg`, `--cal-today-text` - Today indicator
1539
- - `--cal-selected-bg` - Selected date background
1540
- - `--cal-hover`, `--cal-hover-strong` - Hover states
1541
-
1542
- **Sizing:**
1543
- - `--cal-font-size` - Base font size (default: 13px)
1544
- - `--cal-hour-height` - Hour row height in week/day views (default: 60px)
1545
- - `--cal-event-height` - Event bar height (default: 22px)
1546
-
1547
- **Tooltips:**
1548
- - `--cal-tooltip-bg`, `--cal-tooltip-text`, `--cal-tooltip-border` - Tooltip colors
1549
- - `--cal-tooltip-max-width`, `--cal-tooltip-padding`, `--cal-tooltip-radius` - Tooltip sizing
1550
- - `--cal-tooltip-font-size`, `--cal-tooltip-offset` - Tooltip typography
1551
-
1552
- **Loading Overlay:**
1553
- - `--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)
1554
-
1555
- All styles are scoped under `.uc-calendar` to prevent conflicts with your existing CSS.
1556
-
1557
- ## Framework Wrapper APIs
289
+ ## Full Documentation
1558
290
 
1559
- ### React
1560
-
1561
- ```jsx
1562
- import { useRef } from 'react';
1563
- import SimpleCalendarJsReact from 'simple-calendar-js/frameworks/simple-calendar-js-react.jsx';
291
+ For complete documentation including:
292
+ - Detailed configuration options
293
+ - Event object format and timezone handling
294
+ - Drag & drop and resize features
295
+ - Tooltips and HTML content
296
+ - Theming and dark mode
297
+ - Internationalization examples
298
+ - Advanced usage patterns
1564
299
 
1565
- function MyComponent() {
1566
- const calendarRef = useRef();
300
+ **Visit the full documentation at: [https://www.simplecalendarjs.com/docs](https://www.simplecalendarjs.com/docs)**
1567
301
 
1568
- const handleSomeAction = () => {
1569
- // Access imperative methods
1570
- calendarRef.current.goToToday();
1571
- calendarRef.current.setView('week');
1572
- calendarRef.current.navigate(1);
1573
- calendarRef.current.goToDate(new Date('2024-12-25'));
1574
- };
1575
-
1576
- return (
1577
- <SimpleCalendarJsReact
1578
- ref={calendarRef}
1579
- defaultView="month"
1580
- fetchEvents={fetchEvents}
1581
- onEventClick={(event) => console.log(event)}
1582
- onSlotClick={(date) => console.log('Clicked:', date)}
1583
- onViewChange={(view) => console.log('View:', view)}
1584
- onNavigate={(start, end) => console.log('Range:', start, end)}
1585
- />
1586
- );
1587
- }
1588
- ```
302
+ ## Browser Support
1589
303
 
1590
- ### Vue 3
304
+ - Chrome 60+
305
+ - Firefox 60+
306
+ - Safari 12+
307
+ - Edge 79+
1591
308
 
1592
- ```vue
1593
- <template>
1594
- <SimpleCalendarJsVue
1595
- ref="calendar"
1596
- :defaultView="'month'"
1597
- :fetchEvents="fetchEvents"
1598
- @eventClick="handleEventClick"
1599
- @slotClick="handleSlotClick"
1600
- @viewChange="handleViewChange"
1601
- @navigate="handleNavigate"
1602
- />
1603
- </template>
309
+ ## Support & Issues
1604
310
 
1605
- <script>
1606
- export default {
1607
- methods: {
1608
- someAction() {
1609
- // Access imperative methods
1610
- this.$refs.calendar.goToToday();
1611
- this.$refs.calendar.setView('week');
1612
- this.$refs.calendar.navigate(1);
1613
- this.$refs.calendar.goToDate(new Date('2024-12-25'));
1614
- }
1615
- }
1616
- }
1617
- </script>
1618
- ```
311
+ Found a bug or have a feature request? Please open an issue on [GitHub Issues](https://github.com/pclslopes/SimpleCalendarJs/issues).
1619
312
 
1620
- ### Angular
313
+ When reporting bugs, please include:
314
+ - Steps to reproduce
315
+ - Expected vs actual behavior
316
+ - Browser and version
317
+ - Code snippets or examples
1621
318
 
1622
- ```typescript
1623
- import { Component, ViewChild } from '@angular/core';
1624
- import { SimpleCalendarJsComponent } from 'simple-calendar-js/frameworks/simple-calendar-js-angular.component';
319
+ ## Contributing
1625
320
 
1626
- @Component({
1627
- selector: 'app-root',
1628
- template: `
1629
- <simple-calendar-js
1630
- #calendar
1631
- [defaultView]="'month'"
1632
- [fetchEvents]="fetchEvents"
1633
- (eventClick)="handleEventClick($event)"
1634
- (slotClick)="handleSlotClick($event)"
1635
- (viewChange)="handleViewChange($event)"
1636
- (navigate)="handleNavigate($event)"
1637
- ></simple-calendar-js>
1638
- `
1639
- })
1640
- export class AppComponent {
1641
- @ViewChild('calendar') calendar!: SimpleCalendarJsComponent;
1642
-
1643
- someAction() {
1644
- // Access imperative methods
1645
- this.calendar.goToToday();
1646
- this.calendar.setView('week');
1647
- this.calendar.navigate(1);
1648
- this.calendar.goToDate(new Date('2024-12-25'));
1649
- }
1650
- }
1651
- ```
321
+ This project does not accept code contributions or pull requests. All development is handled by the maintainer to ensure code quality and licensing integrity.
1652
322
 
1653
- ## Browser Support
323
+ However, you can help by:
324
+ - Reporting bugs via GitHub Issues
325
+ - Suggesting features
326
+ - Helping other users in discussions
327
+ - Sharing your implementation examples
1654
328
 
1655
- - Chrome 60+
1656
- - Firefox 60+
1657
- - Safari 12+
1658
- - Edge 79+
329
+ See [CONTRIBUTING.md](https://github.com/pclslopes/SimpleCalendarJs/blob/main/CONTRIBUTING.md) for more details.
1659
330
 
1660
331
  ## License
1661
332
 
1662
333
  This project is available for personal, educational, and non-commercial use.
1663
334
 
1664
- **Commercial use requires a separate license.** See [LICENSE](https://unpkg.com/simple-calendar-js/LICENSE) file for full terms.
335
+ **Commercial use requires a separate license.** See [LICENSE](https://github.com/pclslopes/SimpleCalendarJs/blob/main/LICENSE) file for full terms.
1665
336
 
1666
337
  For commercial licensing inquiries: simplecalendarjs@gmail.com or visit [www.simplecalendarjs.com](https://www.simplecalendarjs.com)