Multi-sheet support, sheet ordering, and sheet operations.
Sheet Definition
Each sheet is a self-contained Y.Map entry in the root sheets map, keyed by a SheetId (12-char base64url). The sheet’s name is stored as a Y.Text instance, enabling collaborative editing of the sheet name itself.
// Get sheet name
constsheet=sheets.get(sheetId) asY.Map<unknown>
constname=sheet.get('name') asY.Textconsole.log(name.toString()) // "Sheet 1"
// Rename a sheet (collaborative)
name.delete(0, name.length)
name.insert(0, 'Revenue Data')
Why Y.Text for sheet names?
Using Y.Text instead of a plain string allows two users to concurrently edit a sheet name (e.g. both typing in the rename field). While rare, this prevents the last-writer-wins conflict that a plain string would cause.
Sheet Ordering
Sheet tab order is maintained by the root sheetOrder array:
The position of a SheetId in this array determines its tab position. The first entry is the leftmost tab, etc.
// Get the first sheet
constfirstSheetId=sheetOrder.get(0) asstringconstfirstSheet=sheets.get(firstSheetId) asY.Map<unknown>
// Iterate over all sheets in tab order
for (leti=0; i<sheetOrder.length; i++) {
constsheetId=sheetOrder.get(i) asstringconstsheet=sheets.get(sheetId) asY.Map<unknown>
constname= (sheet.get('name') asY.Text).toString()
console.log(`Tab ${i+1}: ${name}`)
}
Sheet Independence
Each sheet is fully self-contained. All of the following are scoped to individual sheets and do not reference other sheets:
yDoc.transact(() => {
// Remove from tab order
constindex=sheetOrder.toArray().indexOf(sheetId)
if (index!==-1) {
sheetOrder.delete(index, 1)
}
// Remove sheet data
sheets.delete(sheetId)
})
Last sheet protection
The application layer should prevent deleting the last sheet. A Calcillo document must always have at least one sheet.
Reordering Sheets
yDoc.transact(() => {
// Move sheet from position 2 to position 0 (make it first tab)
constsheetId=sheetOrder.get(2) asstringsheetOrder.delete(2, 1)
sheetOrder.insert(0, [sheetId])
})
Duplicating a Sheet
Sheet duplication involves deep-copying all sub-type data into a new sheet with fresh IDs. The new sheet gets a new SheetId, but internal row and column IDs must also be regenerated to avoid ID collisions. Cell content and formatting are copied, but formulas referencing the original sheet’s cells are not automatically updated.
Active Sheet
The currently active (visible) sheet is not stored in the CRDT. Each user can view a different sheet independently. The active sheet is communicated through Yjs awareness: