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/CHANGELOG.md +9 -0
- package/CONTRIBUTING.md +49 -0
- package/README.md +89 -1418
- package/dist/simple-calendar-js.min.css +1 -1
- package/dist/simple-calendar-js.min.js +1 -1
- package/package.json +8 -3
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
|
[](https://www.npmjs.com/package/simple-calendar-js)
|
|
6
|
-
[](https://
|
|
6
|
+
[](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
|
|
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
|
-
##
|
|
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
|
-
|
|
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
|
-
|
|
1066
|
-
|
|
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
|
-
//
|
|
1101
|
-
|
|
262
|
+
// Navigation
|
|
263
|
+
calendar.goToDate(new Date());
|
|
264
|
+
calendar.next();
|
|
265
|
+
calendar.prev();
|
|
266
|
+
calendar.today();
|
|
1102
267
|
|
|
1103
|
-
//
|
|
1104
|
-
calendar.
|
|
268
|
+
// View Management
|
|
269
|
+
calendar.changeView('week');
|
|
270
|
+
calendar.getCurrentView();
|
|
1105
271
|
|
|
1106
|
-
//
|
|
1107
|
-
|
|
272
|
+
// Events
|
|
273
|
+
calendar.addEvent(event);
|
|
274
|
+
calendar.updateEvent(eventId, updates);
|
|
275
|
+
calendar.deleteEvent(eventId);
|
|
276
|
+
calendar.refetchEvents();
|
|
1108
277
|
|
|
1109
|
-
//
|
|
1110
|
-
calendar.
|
|
1111
|
-
|
|
1112
|
-
|
|
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
|
-
|
|
1522
|
-
|
|
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
|
-
|
|
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
|
-
|
|
1560
|
-
|
|
1561
|
-
|
|
1562
|
-
|
|
1563
|
-
|
|
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
|
-
|
|
1566
|
-
const calendarRef = useRef();
|
|
300
|
+
**Visit the full documentation at: [https://www.simplecalendarjs.com/docs](https://www.simplecalendarjs.com/docs)**
|
|
1567
301
|
|
|
1568
|
-
|
|
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
|
-
|
|
304
|
+
- Chrome 60+
|
|
305
|
+
- Firefox 60+
|
|
306
|
+
- Safari 12+
|
|
307
|
+
- Edge 79+
|
|
1591
308
|
|
|
1592
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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://
|
|
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)
|