Case 6: 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
Section titled “Implementation Guide”🎨 renderCallback for Stable Layout
Section titled “🎨 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
Section titled “📋 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
Section titled “🚫 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
Section titled “💾 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
Section titled “🗂️ 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 dayif (...) { events = parseEvents('...');}week.push(events);✅ Summary of Role Division
Section titled “✅ 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 |