Skip to content

Commit 424ea37

Browse files
pclslopesclaude
andcommitted
add rich HTML tooltip system with fixed positioning and enable HTML by default
- Reimagined tooltip system using DOM elements instead of CSS pseudo-elements - HTML support with comprehensive sanitization (tags, URLs, CSS) - Fixed positioning attached to document.body prevents calendar clipping - Intelligent edge detection repositions tooltips based on viewport - Customizable styling: background color, text color, max width, delay - Changed defaults: allowHtmlInEvents and tooltipAllowHtml now true - Updated documentation in README and docs.html Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
1 parent 2e38f4d commit 424ea37

6 files changed

Lines changed: 812 additions & 149 deletions

File tree

CHANGELOG.md

Lines changed: 49 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,55 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
99

1010
No unreleased changes yet.
1111

12-
## [3.0.11] - 2026-03-03
12+
## [3.0.11] - 2026-03-04
13+
14+
### Added
15+
- **Rich HTML tooltip system** - Reimagined tooltip system with HTML support, fixed positioning, and enhanced customization
16+
- **HTML formatting support** - Tooltips now support rich HTML content with secure sanitization:
17+
- Allowed tags: `<b>`, `<i>`, `<strong>`, `<em>`, `<span>`, `<br>`, `<a>`
18+
- Links (`<a>` tags) with URL validation (blocks `javascript:`, `data:`, `vbscript:` protocols)
19+
- Inline color styles via `<span style="color: ...">` (sanitized to prevent CSS injection)
20+
- Automatic `target="_blank"` and `rel="noopener noreferrer"` on all links for security
21+
- Per-event HTML override via `tooltipAllowHtml` property
22+
- Global control via new `tooltipAllowHtml` option (default: `true`)
23+
- **Fixed positioning** - Tooltips use `position: fixed` and attach to `document.body`:
24+
- Never clipped by calendar container's `overflow: hidden`
25+
- Intelligent edge detection prevents overflow outside viewport
26+
- Tooltips reposition above/below and left/right based on available space
27+
- Arrow dynamically points to hovered element center
28+
- **Customizable styling** - New configuration options for tooltip appearance:
29+
- `tooltipBgColor` - Global background color (default: `null` uses CSS variable)
30+
- `tooltipTextColor` - Global text color (default: `null` uses CSS variable)
31+
- `tooltipMaxWidth` - Maximum width in pixels (default: `250`)
32+
- `tooltipDelay` - Show delay in milliseconds (default: `400`)
33+
- **Multiline support** - Natural multiline rendering with `<br>` tags or line breaks
34+
- **DOM-based architecture** - Replaced CSS pseudo-elements (`::before`/`::after`) with real DOM elements:
35+
- Singleton pattern (one tooltip element per calendar instance)
36+
- Efficient memory management with automatic cleanup in `destroy()`
37+
- Better performance with reusable tooltip element
38+
39+
### Changed
40+
- **Tooltip implementation** - Migrated from CSS pseudo-element system to DOM-based tooltips
41+
- Old `::before` and `::after` tooltip styles commented out (deprecated but preserved for reference)
42+
- Tooltips now render as separate DOM elements for HTML support
43+
- Backward compatible: text-only tooltips work unchanged when `tooltipAllowHtml` is `false`
44+
- **Default HTML enabled** - HTML rendering now enabled by default for both events and tooltips
45+
- `allowHtmlInEvents` default changed from `false` to `true`
46+
- `tooltipAllowHtml` default changed from `false` to `true`
47+
- Event titles and tooltips now support HTML formatting by default (bold, italic, links, colors, etc.)
48+
- All HTML is sanitized for security (comprehensive XSS protection)
49+
- To disable HTML, explicitly set `allowHtmlInEvents: false` and/or `tooltipAllowHtml: false`
50+
51+
### Security
52+
- **Enhanced HTML sanitization** - Added comprehensive security measures for HTML tooltips:
53+
- DOM-based parsing (browser handles HTML edge cases safely)
54+
- Tag allowlist (only safe formatting tags permitted)
55+
- URL protocol validation (blocks dangerous schemes)
56+
- CSS sanitization (only `color` properties allowed, blocks `expression`, `url()`, `import`)
57+
- Automatic event handler stripping (prevents inline JavaScript)
58+
- Recursive node cleaning (prevents nesting attacks)
59+
60+
## [3.0.10] - 2026-03-03
1361

1462
### Added
1563
- **Auto-contrast text color** - New `autoContrastText` option automatically calculates optimal text color based on event background color using WCAG luminance formula

README.md

Lines changed: 123 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -217,13 +217,18 @@ export class CalendarComponent {
217217
| `showYearPicker` | boolean | `true` | Enable year picker dropdown (month view) |
218218
| `showViewSwitcher` | boolean | `true` | Show view switcher buttons |
219219
| `showTooltips` | boolean | `true` | Show tooltips on hover for events |
220+
| `tooltipAllowHtml` | boolean | `true` | Allow HTML in tooltips (sanitized for security). Supports: `<b>`, `<i>`, `<strong>`, `<em>`, `<span>`, `<br>`, `<a>` with safe attributes and URL validation |
221+
| `tooltipBgColor` | string \| null | `null` | Custom tooltip background color (hex). If `null`, uses CSS variable `--cal-tooltip-bg` |
222+
| `tooltipTextColor` | string \| null | `null` | Custom tooltip text color (hex). If `null`, uses CSS variable `--cal-tooltip-text` |
223+
| `tooltipMaxWidth` | number | `250` | Maximum tooltip width in pixels |
224+
| `tooltipDelay` | number | `400` | Delay in milliseconds before tooltip appears on hover |
220225
| `listDaysForward` | number | `30` | Number of days forward to show in list view |
221226
| `enabledViews` | string[] | `['month', 'week', 'day']` | Available view modes. Add `'list'` to enable list view |
222227
| `enableDragDrop` | boolean | `false` | Enable drag and drop to move events |
223228
| `enableResize` | boolean | `false` | Enable resizing events to change duration |
224229
| `autoContrastText` | boolean | `false` | Automatically calculate contrasting text color based on event background color for better readability |
225230
| `contrastLevel` | string | `'high'` | Text contrast level when `autoContrastText` is enabled: `'high'` (black/white), `'medium'` (darker/lighter shade), `'low'` (subtle variation) |
226-
| `allowHtmlInEvents` | boolean | `false` | Allow basic HTML tags in event titles (sanitized for security). Supports: `<b>`, `<i>`, `<strong>`, `<em>`, `<span>`, `<br>` with `class` attribute for icons |
231+
| `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 |
227232
| `monthTimedEventStyle` | string | `'list'` | Display style for timed events in month view: `'list'` (schedule format) or `'block'` (traditional blocks) |
228233
| `monthDayNumberAlign` | string | `'left'` | Horizontal alignment of day numbers in month view cells: `'left'`, `'center'`, or `'right'` |
229234
| `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 |
@@ -249,7 +254,8 @@ interface CalendarEvent {
249254
textColor?: string; // Custom text color (hex, optional) - overrides autoContrastText
250255
allowHtml?: boolean; // Override global allowHtmlInEvents setting for this event (optional)
251256
description?: string; // Event description (also used for tooltip if tooltip not provided)
252-
tooltip?: string; // Custom tooltip text shown on hover (overrides description)
257+
tooltip?: string; // Custom tooltip text shown on hover (can contain HTML if tooltipAllowHtml is enabled)
258+
tooltipAllowHtml?: boolean; // Override global tooltipAllowHtml setting for this event's tooltip (optional)
253259
[key: string]: any; // Any additional custom properties
254260
}
255261
```
@@ -365,17 +371,17 @@ fetchEvents: async (start, end) => {
365371

366372
## Tooltips
367373

368-
SimpleCalendarJs includes built-in tooltip support for displaying additional event information on hover.
374+
SimpleCalendarJs includes a powerful tooltip system with **HTML support**, smart positioning, and extensive customization options.
369375

370376
### How Tooltips Work
371377

372-
Tooltips appear when you hover over an event for 400ms. They display content based on the following priority:
378+
Tooltips appear when you hover over an event (default 400ms delay). They display content based on the following priority:
373379

374-
1. **`tooltip` property** (highest priority) - Custom tooltip text
380+
1. **`tooltip` property** (highest priority) - Custom tooltip content
375381
2. **`description` property** - Falls back if no tooltip is provided
376382
3. **`title` property** - Falls back if neither tooltip nor description is provided
377383

378-
### Basic Usage
384+
### Basic Text Tooltips
379385

380386
```javascript
381387
const events = [
@@ -384,68 +390,105 @@ const events = [
384390
title: 'Team Meeting',
385391
start: new Date('2024-03-15T10:00:00'),
386392
end: new Date('2024-03-15T11:00:00'),
387-
tooltip: 'Weekly team sync\nDiscuss Q1 roadmap' // Custom tooltip
393+
tooltip: 'Weekly team sync' // Simple text tooltip
388394
},
389395
{
390396
id: 2,
391397
title: 'Client Call',
392398
start: new Date('2024-03-15T14:00:00'),
393399
end: new Date('2024-03-15T15:00:00'),
394-
description: 'Requirements gathering\nPrepare demo' // Used as tooltip
395-
},
396-
{
397-
id: 3,
398-
title: 'Code Review',
399-
start: new Date('2024-03-15T16:00:00'),
400-
end: new Date('2024-03-15T17:00:00')
401-
// No tooltip/description - will show title
400+
description: 'Requirements gathering session' // Used as tooltip
402401
}
403402
];
404403
```
405404

406-
### Multiline Tooltips
405+
### Rich HTML Tooltips
407406

408-
Use `\n` (newline character) in your tooltip or description text to create multiple lines:
407+
**By default**, tooltips support HTML formatting with secure sanitization:
409408

410409
```javascript
411410
{
412411
id: 1,
413-
title: 'Project Kickoff',
414-
start: new Date('2024-03-20T09:00:00'),
415-
end: new Date('2024-03-20T10:30:00'),
416-
tooltip: 'Project Kickoff Meeting\n\nAgenda:\n- Introductions\n- Timeline review\n- Q&A session\n\nLocation: Conference Room A'
412+
title: 'Design Review',
413+
start: new Date('2024-03-20T14:00:00'),
414+
end: new Date('2024-03-20T15:30:00'),
415+
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>'
417416
}
418417
```
419418

420-
### Tooltip Features
419+
**Supported HTML tags** (all sanitized for security):
420+
- `<b>`, `<strong>` - Bold text
421+
- `<i>`, `<em>` - Italic text
422+
- `<br>` - Line breaks
423+
- `<span style="color: #xxx;">` - Colored text (only `color` and `background-color` allowed)
424+
- `<a href="...">` - Links (automatically open in new tab, URL validated for safety)
421425

422-
- **Smart Positioning**: Tooltips automatically adjust their position to stay visible:
423-
- Events near the **top** of the viewport: tooltip shows below
424-
- Events near the **right edge**: tooltip aligns to the right
425-
- Events near the **left edge**: tooltip aligns to the left
426+
**Security features**:
427+
- Dangerous protocols blocked (`javascript:`, `data:`, `vbscript:`)
428+
- Only safe CSS properties allowed in `style` attributes
429+
- All event handlers stripped (`onclick`, etc.)
430+
- Script tags and other dangerous elements removed
426431

427-
- **Visual Design**:
428-
- Dark background with rounded corners
429-
- Small arrow pointing to the event
430-
- Smooth fade-in animation (400ms delay)
431-
- Supports both light and dark themes
432+
### Multiline Tooltips
432433

433-
- **Responsive**: Tooltips overflow outside calendar boundaries to ensure full visibility
434+
Use `<br>` tags for HTML tooltips, or `\n` for plain text:
434435

435-
### Disabling Tooltips
436+
```javascript
437+
// HTML approach (recommended)
438+
{
439+
tooltip: '<b>Meeting Agenda:</b><br>• Introductions<br>• Timeline review<br>• Q&A session'
440+
}
436441

437-
You can disable tooltips globally using the `showTooltips` option:
442+
// Plain text approach
443+
{
444+
tooltipAllowHtml: false, // Disable HTML for this event
445+
tooltip: 'Meeting Agenda:\n• Introductions\n• Timeline review\n• Q&A session'
446+
}
447+
```
448+
449+
### Configuration Options
450+
451+
Control tooltip behavior globally:
438452

439453
```javascript
440454
const calendar = new SimpleCalendarJs('#calendar', {
441-
showTooltips: false, // Disable all tooltips
455+
showTooltips: true, // Enable/disable tooltips (default: true)
456+
tooltipAllowHtml: true, // Allow HTML in tooltips (default: true)
457+
tooltipBgColor: '#1f2937', // Custom background color (default: null = CSS var)
458+
tooltipTextColor: '#f9fafb', // Custom text color (default: null = CSS var)
459+
tooltipMaxWidth: 250, // Maximum width in pixels (default: 250)
460+
tooltipDelay: 400, // Hover delay in milliseconds (default: 400)
442461
fetchEvents: async (start, end) => { ... }
443462
});
444463
```
445464

465+
### Per-Event HTML Override
466+
467+
Override the global `tooltipAllowHtml` setting for specific events:
468+
469+
```javascript
470+
{
471+
id: 1,
472+
title: 'Secure Event',
473+
tooltip: '<script>alert("xss")</script>', // This will be sanitized/removed
474+
tooltipAllowHtml: false // Force plain text for this event only
475+
}
476+
```
477+
478+
### Smart Positioning
479+
480+
Tooltips use **fixed positioning** and automatically adjust to stay visible:
481+
482+
- **Fixed to viewport**: Never clipped by calendar container boundaries
483+
- **Edge detection**:
484+
- Near **top**: tooltip appears below the event
485+
- Near **right edge**: tooltip shifts left
486+
- Near **left edge**: tooltip shifts right
487+
- **Dynamic arrow**: Arrow always points to the hovered event center
488+
446489
### Styling Tooltips
447490

448-
Tooltips use CSS custom properties and can be customized:
491+
Customize via CSS variables:
449492

450493
```css
451494
:root {
@@ -457,12 +500,50 @@ Tooltips use CSS custom properties and can be customized:
457500
--cal-tooltip-radius: 6px; /* Border radius */
458501
--cal-tooltip-font-size: 12px; /* Font size */
459502
--cal-tooltip-offset: 8px; /* Distance from event */
503+
--cal-tooltip-shadow: 0 4px 12px rgba(0, 0, 0, 0.15); /* Shadow */
460504
}
461505
```
462506

463-
### Limitations
507+
Or use configuration options for colors:
464508

465-
- Tooltips display **plain text only** (no HTML rendering)
509+
```javascript
510+
const calendar = new SimpleCalendarJs('#calendar', {
511+
tooltipBgColor: '#dc2626', // Red background
512+
tooltipTextColor: '#ffffff', // White text
513+
});
514+
```
515+
516+
### Disabling HTML
517+
518+
To use only plain text tooltips globally:
519+
520+
```javascript
521+
const calendar = new SimpleCalendarJs('#calendar', {
522+
tooltipAllowHtml: false, // Disable HTML rendering
523+
});
524+
```
525+
526+
### Advanced Example
527+
528+
```javascript
529+
{
530+
id: 1,
531+
title: 'Client Demo',
532+
start: new Date('2024-03-25T10:00:00'),
533+
end: new Date('2024-03-25T11:00:00'),
534+
tooltip: `
535+
<span style="color: #ef4444;"><b>⚡ HIGH PRIORITY</b></span><br><br>
536+
<b>Q4 Business Review</b><br>
537+
<i>Executive presentation</i><br><br>
538+
<b>Must prepare:</b><br>
539+
• Financial projections<br>
540+
• ROI analysis<br>
541+
• Growth charts<br><br>
542+
<a href="https://example.com/deck">View presentation deck</a>
543+
`,
544+
tooltipAllowHtml: true
545+
}
546+
```
466547
- Special characters are automatically escaped for security
467548
- Maximum width is 250px by default (can be customized via CSS variables)
468549

@@ -865,12 +946,11 @@ const calendar = new SimpleCalendarJs('#calendar', {
865946

866947
### Important Notes
867948

868-
- **Default behavior**: HTML is disabled by default (`allowHtmlInEvents: false`)
949+
- **Default behavior**: HTML is **enabled by default** (`allowHtmlInEvents: true`, `tooltipAllowHtml: true`)
869950
- **Security first**: All HTML is sanitized - dangerous tags and attributes are stripped
870-
- **Class attribute only**: Only the `class` attribute is preserved (for icons)
871-
- **No inline styles**: `style` attributes are removed for security
872-
- **Plain text fallback**: If `allowHtmlInEvents` is false, HTML is escaped and displayed as text
873-
- **Tooltips**: HTML is NOT rendered in tooltips (security consideration)
951+
- **Event titles**: Only `class` attribute is preserved (for icons). `style` attributes are removed
952+
- **Tooltips**: Support both `class` and safe `style` attributes (only `color` and `background-color` properties allowed)
953+
- **Plain text fallback**: If `allowHtmlInEvents`/`tooltipAllowHtml` is false, HTML is escaped and displayed as text
874954

875955
### Best Practices
876956

0 commit comments

Comments
 (0)