Thank you for being patient! We're working hard on resolving the issue
Subcolumn plugins are child plugins that render inside a parent layout row. The common case is a child row contributing timed items to a calendar row.
The parent discovers child rows from the sheet tree and asks the child's plugin for a provider:
Calendar row bind
→ row.children()
→ handle.pluginManager.getPlugin(child.type)
→ childPlugin.contentProviders.calendar
→ provider.subcolumnFromRow(sheet, child, parentRow)
→ Calendar layout renders the returned subcolumn
Your plugin is responsible for item selection and cell appearance. The parent is responsible for layout and positioning.
CalendarSubcolumn.ProviderThe calendar key on contentProviders uses this contract:
interface Provider<EventItem, FullDayEventItem = unknown> {
subcolumnFromRow(
sheet: SheetViewModel.Readable,
row: SheetRow.Joined.Client,
parentRow: SheetRow.Joined.Client,
): CalendarSubcolumn<EventItem>;
fullDayEventsFromRow?(
row: SheetRow.Joined.Client,
range: NaiveDate.Range,
): FullDayEventItem[];
}
subcolumnFromRow() is where you capture row-specific filters or indexes from
the child row and return a renderable subcolumn object.
This is the common pattern for timed events or tasks:
import { RowId } from "@tento-lona";
import { CalendarSubcolumn } from "@tento-lona/sheets-ui";
contentProviders: ContentProviders = {
calendar: {
subcolumnFromRow: (_sheet, row, _parentRow) => ({
rowId: RowId.fromLocalId(row.id),
renderer: "cells",
events: (range) =>
row.assertVariant<TasksVariant.Type>().tasksIndex?.findAllInRange(range) ?? [],
gestureItem: (_item) => null,
bindCell: ($cell, item, { windowed, currentTime }) => {
TasksPluginBase.bindTaskCellContent($cell, item, windowed, currentTime);
},
}),
},
};
Two details matter:
rowId on the returned subcolumn is a RowId, so child plugins commonly
convert with RowId.fromLocalId(row.id)For bars that stretch across dates, return a span renderer instead:
calendar: {
subcolumnFromRow: (sheet, row, _parentRow) => ({
rowId: RowId.fromLocalId(row.id),
renderer: "spans",
makeSpans: this.makeChartSpans(sheet, row),
makeLayout: SpanColumn.make,
}),
},
Use this when the item is better represented as a bar than a timed cell.
If the child row also contributes items to the parent's all-day area, implement
fullDayEventsFromRow():
calendar: {
subcolumnFromRow: (_sheet, row, _parentRow) => this.makeTimedSubcolumn(row),
fullDayEventsFromRow: (row, range) => {
const idx = row.assertVariant<CalendarListVariant.Type>().eventsIndex;
return idx?.findFullDayEventsInRange(range) ?? [];
},
},
The parent can then aggregate full-day items from every qualifying child.
Register the child plugin normally:
pluginManager.plugin(new TaskListPlugin(taskStore));
There is no separate "subcolumn plugin registry". Participation is determined
by the row tree and the presence of contentProviders.calendar.
This is what the parent side usually looks like:
for (const child of row.children()) {
const childPlugin = handle.pluginManager.getPlugin(child.type);
const provider = childPlugin.contentProviders?.calendar;
if (!provider) continue;
const subcolumn = provider.subcolumnFromRow(sheet, child, row);
subcolumns.push(subcolumn);
}
If your child row is nested correctly and its plugin exposes calendar, the
parent will discover it automatically.