Skip to content

Commit f93441d

Browse files
pclslopesclaude
andcommitted
version 3.0.6: scrollable views, auto-calculate events, and UX improvements
Major Features: - Add auto-calculate for maxEventsPerCell (new default: -1) - Intelligently calculates how many events fit based on cell height - Fully responsive to calendar size and screen dimensions - Options: -1 (auto), 0 (unlimited), 1+ (fixed number) - Implement scrollable views with fixed headers - Week/day/list views have max-height: 650px with scrollable content - Headers and toolbar stay fixed while scrolling - Month view maintains natural expanding height - Consistent custom scrollbar styling across all views Improvements: - Week/day all-day events now always show titles on continuation segments - List view sticky date headers have opaque background (no transparency) - Calendar maintains height during loading (no layout shift) - Added --cal-bg-secondary CSS variable for consistent backgrounds Bug Fixes: - Fixed calendar rebuilding entire shell during event fetching - Prevented page content jumping when loading events - Improved loading spinner overlay behavior Documentation: - Updated README.md with new maxEventsPerCell options - Updated docs.html with complete feature documentation - Updated index.html demo to support -1 value Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
1 parent 7d5acbb commit f93441d

6 files changed

Lines changed: 92 additions & 20 deletions

File tree

CHANGELOG.md

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
77

88
## [Unreleased]
99

10+
No unreleased changes yet.
11+
1012
## [3.0.6] - 2026-02-24
1113

1214
### Added
@@ -17,6 +19,22 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1719
- Touch support for drag and drop on mobile/tablet devices
1820
- Visual feedback during drag operations with 15-minute snap-to-grid in week/day views
1921
- Cross-boundary conversion (timed ↔ all-day events when dragging between sections)
22+
- Auto-calculate feature for `maxEventsPerCell`: set to `-1` to automatically calculate how many events fit based on cell height
23+
24+
### Changed
25+
- **`maxEventsPerCell` default changed from `3` to `-1`** (auto-calculate mode)
26+
- `-1` = Auto-calculate based on cell height (responsive, adapts to calendar size)
27+
- `0` = Show all events, cells expand infinitely
28+
- `1+` = Show exactly that number, rest in "+N more"
29+
- Week, day, and list views now have a max-height of 650px and scroll content while keeping headers and toolbar fixed in place
30+
- Month view maintains its natural expanding height based on number of weeks and events per cell
31+
- All scrollable views use consistent custom scrollbar styling
32+
- Calendar adapts to container height: if you set a custom height on the container div, week/day/list views will respect it
33+
- Week/day view all-day events now always display titles on continuation segments (matches day view behavior)
34+
35+
### Fixed
36+
- List view sticky date headers now have opaque background to prevent events showing through when scrolling
37+
- Calendar no longer rebuilds entire shell during event fetching, preventing layout shift and page content jumping
2038

2139
## [3.0.5] - 2026-02-23
2240

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -168,7 +168,7 @@ export class CalendarComponent {
168168
| `showTimeInItems` | boolean | `true` | Show time in event items |
169169
| `showGridLines` | boolean | `true` | Show calendar grid lines |
170170
| `showBorder` | boolean | `true` | Show calendar outer border |
171-
| `maxEventsPerCell` | number | `3` | Maximum events per cell in month view before showing "+N more". Set to `0` for unlimited (show all events) |
171+
| `maxEventsPerCell` | number | `-1` | Maximum events per cell in month view. `-1` = auto-calculate based on cell height (responsive), `0` = unlimited (show all events), `1+` = show exactly that number |
172172
| `showToolbar` | boolean | `true` | Show the toolbar |
173173
| `showTodayButton` | boolean | `true` | Show "Today" button |
174174
| `showNavigation` | boolean | `true` | Show prev/next navigation arrows |

css/simple-calendar-js.css

Lines changed: 23 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/**
2-
* SimpleCalendarJs v3.0.5 — simple-calendar-js.css
2+
* SimpleCalendarJs v3.0.6 — simple-calendar-js.css
33
* A clean, modern, and feature-rich JavaScript calendar component with zero dependencies
44
*
55
* @author Pedro Lopes <simplecalendarjs@gmail.com>
@@ -13,6 +13,7 @@
1313
/* ===== CUSTOM PROPERTIES (Theming Tokens) ===== */
1414
:root {
1515
--cal-bg: #ffffff;
16+
--cal-bg-secondary: #f9fafb;
1617
--cal-text: #111827;
1718
--cal-text-subtle: #6b7280;
1819
--cal-text-muted: #9ca3af;
@@ -74,11 +75,10 @@
7475
background: var(--cal-bg);
7576
border: 1px solid var(--cal-border);
7677
border-radius: var(--cal-radius);
77-
overflow: visible;
78+
overflow: hidden;
7879
display: flex;
7980
flex-direction: column;
8081
position: relative;
81-
min-height: 500px;
8282
-webkit-font-smoothing: antialiased;
8383
}
8484

@@ -573,8 +573,8 @@
573573
.uc-day-view {
574574
display: flex;
575575
flex-direction: column;
576-
flex: 1;
577-
overflow: visible;
576+
max-height: 650px;
577+
overflow: hidden;
578578
border-radius: 0 0 var(--cal-radius) var(--cal-radius);
579579
}
580580

@@ -875,10 +875,26 @@
875875

876876
.uc-list-view {
877877
padding: 0;
878-
overflow: visible;
878+
max-height: 650px;
879+
overflow-y: auto;
880+
overflow-x: hidden;
879881
border-radius: 0 0 var(--cal-radius) var(--cal-radius);
880882
}
881883

884+
/* Scrollbar styling for list view */
885+
.uc-list-view::-webkit-scrollbar {
886+
width: 6px;
887+
}
888+
889+
.uc-list-view::-webkit-scrollbar-track {
890+
background: transparent;
891+
}
892+
893+
.uc-list-view::-webkit-scrollbar-thumb {
894+
background: var(--cal-border-strong);
895+
border-radius: 3px;
896+
}
897+
882898
.uc-list-empty {
883899
display: flex;
884900
align-items: center;
@@ -1167,6 +1183,7 @@
11671183
.uc-calendar.uc-dark,
11681184
.uc-dark .uc-calendar {
11691185
--cal-bg: #1f2937;
1186+
--cal-bg-secondary: #111827;
11701187
--cal-text: #f9fafb;
11711188
--cal-text-subtle: #9ca3af;
11721189
--cal-text-muted: #6b7280;

docs.html

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -414,8 +414,8 @@ <h3>View Options</h3>
414414
<tr>
415415
<td><code>maxEventsPerCell</code></td>
416416
<td>Number</td>
417-
<td><code>3</code></td>
418-
<td>Maximum events per cell in month view before showing "+N more". Set to <code>0</code> for unlimited (show all events)</td>
417+
<td><code>-1</code></td>
418+
<td>Maximum events per cell in month view. <code>-1</code> = auto-calculate based on cell height (responsive), <code>0</code> = unlimited (show all events), <code>1+</code> = show exactly that number</td>
419419
</tr>
420420
</tbody>
421421
</table>

index.html

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -712,8 +712,8 @@ <h1>SimpleCalendarJs</h1>
712712

713713
<div class="option-group">
714714
<span class="option-label">Max Events Per Cell</span>
715-
<input type="number" class="option-select" id="opt-max-events" min="0" max="20" value="3" style="width: 80px; padding: 6px 10px;">
716-
<span style="font-size: 11px; color: var(--text-muted); margin-left: 8px;">(0 = unlimited)</span>
715+
<input type="number" class="option-select" id="opt-max-events" min="-1" max="20" value="-1" style="width: 80px; padding: 6px 10px;">
716+
<span style="font-size: 11px; color: var(--text-muted); margin-left: 8px;">(-1 = auto, 0 = unlimited)</span>
717717
</div>
718718

719719
<div class="option-group">
@@ -1006,7 +1006,7 @@ <h1>SimpleCalendarJs</h1>
10061006
showGridLines: true,
10071007
showBorder: true,
10081008
showTooltips: true,
1009-
maxEventsPerCell: 3,
1009+
maxEventsPerCell: -1,
10101010
listDaysForward: 30,
10111011
showToolbar: true,
10121012
showTodayButton: true,
@@ -1238,12 +1238,13 @@ <h1>SimpleCalendarJs</h1>
12381238

12391239
maxEventsInput.addEventListener('change', () => {
12401240
const value = parseInt(maxEventsInput.value, 10);
1241-
if (isNaN(value) || value < 0) {
1241+
if (isNaN(value) || value < -1) {
12421242
maxEventsInput.value = opts.maxEventsPerCell;
12431243
return;
12441244
}
12451245
opts.maxEventsPerCell = value;
1246-
log('opt', 'tag-opt', `maxEventsPerCell → ${opts.maxEventsPerCell}${value === 0 ? ' (unlimited)' : ''}`);
1246+
const modeLabel = value === 0 ? ' (unlimited)' : value === -1 ? ' (auto)' : '';
1247+
log('opt', 'tag-opt', `maxEventsPerCell → ${opts.maxEventsPerCell}${modeLabel}`);
12471248
createCalendar();
12481249
});
12491250

js/simple-calendar-js.js

Lines changed: 42 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/**
2-
* SimpleCalendarJs v3.0.5
2+
* SimpleCalendarJs v3.0.6
33
* A clean, modern, and feature-rich JavaScript calendar component with zero dependencies
44
*
55
* @author Pedro Lopes <simplecalendarjs@gmail.com>
@@ -372,7 +372,7 @@
372372
showViewSwitcher: true,
373373
showTooltips: true,
374374
showBorder: true,
375-
maxEventsPerCell: 3,
375+
maxEventsPerCell: -1,
376376
listDaysForward: 30,
377377
enabledViews: ['month', 'week', 'day'],
378378
enableDragDrop: false,
@@ -443,6 +443,9 @@
443443
}
444444
}
445445

446+
// Build the shell once during initialization
447+
this._root.innerHTML = this._buildShell();
448+
446449
this._fetchAndRender();
447450
this._startNowUpdater();
448451
}
@@ -555,8 +558,8 @@
555558
}
556559

557560
async _fetchAndRender() {
558-
// Build the shell immediately (updates toolbar title + clears view)
559-
this._root.innerHTML = this._buildShell();
561+
// Update toolbar only (don't rebuild entire shell)
562+
this._updateToolbar();
560563

561564
if (!this._opts.fetchEvents) {
562565
this._renderView();
@@ -630,6 +633,10 @@
630633
this._root.classList.add('uc-unlimited-events');
631634
// Remove cell min-height override for unlimited mode
632635
this._root.style.removeProperty('--cal-cell-min-height');
636+
} else if (this._opts.maxEventsPerCell === -1) {
637+
this._root.classList.remove('uc-unlimited-events');
638+
// Auto-calculate mode: use default CSS value, don't override
639+
this._root.style.removeProperty('--cal-cell-min-height');
633640
} else {
634641
this._root.classList.remove('uc-unlimited-events');
635642
// Set cell min-height based on maxEventsPerCell to match maximum row height
@@ -685,6 +692,18 @@
685692
`;
686693
}
687694

695+
_updateToolbar() {
696+
const toolbar = this._root.querySelector('.uc-toolbar');
697+
if (toolbar) {
698+
const temp = document.createElement('div');
699+
temp.innerHTML = this._buildToolbar();
700+
const newToolbar = temp.firstElementChild;
701+
if (newToolbar) {
702+
toolbar.replaceWith(newToolbar);
703+
}
704+
}
705+
}
706+
688707
_buildToolbar() {
689708
if (!this._opts.showToolbar) return '';
690709

@@ -857,7 +876,24 @@
857876

858877
_buildWeekRow(weekDates, allEvents) {
859878
const curMonth = this._date.getMonth();
860-
const MAX = this._opts.maxEventsPerCell === 0 ? Infinity : this._opts.maxEventsPerCell;
879+
880+
// Calculate MAX based on maxEventsPerCell option
881+
let MAX;
882+
if (this._opts.maxEventsPerCell === 0) {
883+
MAX = Infinity; // Show all events
884+
} else if (this._opts.maxEventsPerCell === -1) {
885+
// Auto-calculate based on cell height
886+
// Get the computed cell min-height (default is 112px from CSS)
887+
const computedStyle = getComputedStyle(this._root);
888+
const cellMinHeight = parseInt(computedStyle.getPropertyValue('--cal-cell-min-height') || '112');
889+
// Formula: (cellHeight - headerHeight - moreLink) / eventHeightWithGap
890+
// cellHeight - 30 (header) - 28 (+N more space) / 24 (event + gap)
891+
MAX = Math.floor((cellMinHeight - 30 - 28) / 24);
892+
// Ensure at least 1 event is shown
893+
MAX = Math.max(1, MAX);
894+
} else {
895+
MAX = this._opts.maxEventsPerCell; // Use specified number
896+
}
861897

862898
// Separate multi-day from single-day events
863899
const multiDay = allEvents.filter(isMultiDay);
@@ -1146,7 +1182,7 @@
11461182
style="left:calc(${left}% + 2px);width:calc(${width}% - 4px);top:${topCss};background:${esc(color)};border-radius:${rl} ${rr} ${rr} ${rl};"
11471183
data-event-id="${esc(event.id)}" data-action="event-click"
11481184
${tooltipAttr}>
1149-
${isStart ? `<span class="uc-event-title">${esc(event.title)}</span>` : '&nbsp;'}
1185+
<span class="uc-event-title">${esc(event.title)}</span>
11501186
${resizeHandle}
11511187
</div>`;
11521188
}

0 commit comments

Comments
 (0)