Skip to content

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.

📄 View Source Code

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 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>
))}
</>
);
},

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 day
if (...) {
events = parseEvents('...');
}
week.push(events);
HookResponsibility
renderCallbackOuter layout (container, date header) — always applied
renderArrayRenders event badges when value is an array
renderNullRenders “No events” placeholder when value is null
deserializeFirstConverts empty string input back to null