๐๏ธ Calendar/Scheduler Interface
This demo showcases a calendar/scheduler UI built with GridSheet.
It demonstrates custom date cell rendering with event management.
Users can input events in "HH:MM Task" format, separated by newlines.
Implementation Guide
๐ View Source Code๐จ renderCallback for Stable Layout
The outer cell layout (padding, flexbox container, date header) is defined in renderCallback, which wraps any rendered result regardless of the cell's value type.
This prevents layout inconsistency when the cell holds different types of values (array, null, string, etc.).
renderCallback(rendered, { point }) {
const { date } = getDateFromPosition(point.y, point.x);
return (
<div style={{ width: '100%', height: '100%', padding: 8, display: 'flex', flexDirection: 'column' }}>
<div style={{ fontWeight: 700, fontSize: 14 }}>{date}</div>
<div style={{ flex: 1, overflow: 'hidden' }}>{rendered}</div>
</div>
);
},๐ renderArray for Task List Only
renderArray focuses solely on rendering the list of event items.
It does not need to handle layout or the empty state โ those are delegated to renderCallback and renderNull respectively.
renderArray({ value }) {
const events = Array.isArray(value) ? value : [];
return (
<>
{events.map((event, i) => (
<div key={i} style={{ background: '#3498db', color: '#fff', borderRadius: 3, padding: '2px 4px' }}>
{event.time && <span>{event.time}</span>}
<span>{event.task}</span>
</div>
))}
</>
);
},๐ซ renderNull for Empty State
When a cell has no events, its value is null. renderNull handles this case and renders the "No events" placeholder.
This keeps the empty state display separate from the array rendering logic.
renderNull() {
return (
<span style={{ color: '#aaa', fontSize: 11, fontStyle: 'italic', textAlign: 'center' }}>
No events
</span>
);
},๐พ deserializeFirst โ Empty String โ null
When a user clears a cell, the value becomes an empty string.
deserializeFirst converts that to null so that renderNull (not renderArray) is invoked.
deserializeFirst: (value: string) => {
if (!value || !value.trim()) {
return { value: null };
}
const events = parseEvents(value);
return { value: events };
},๐๏ธ Initial Data Uses null for Empty Days
Cells with no events are initialized as null (not []), ensuring consistent behavior between the initial render and after a user clears a cell.
let events: EventType[] | null = null;
// only assign if there are events for this day
if (...) {
events = parseEvents('...');
}
week.push(events);โ Summary of Role Division
| Hook | Responsibility |
|---|---|
renderCallback | Outer layout (container, date header) โ always applied |
renderArray | Renders event badges when value is an array |
renderNull | Renders "No events" placeholder when value is null |
deserializeFirst | Converts empty string input back to null |