[]
SpreadJS collaboration adopts the OT (Operational Transformation) approach, implementing an OT_Type that meets SpreadJS collaboration requirements based on the constraints outlined in js-collaboration-ot . It also includes additional features. The codebase is divided into two packages for front-end and back-end:
spread-sheets-collaboration-client: Handles client-side capabilities.
spread-sheets-collaboration: Handles server-side capabilities.
Only npm installation is provided, with two packages available:
npm install @mescius/spread-sheets-collaboration-client
npm install @mescius/spread-sheets-collaborationThe spread-sheets-collaboration-client package provides the following capabilities:
Implements OT_Type
Provides simple binding between workbook and Doc
Filters out Ops that do not require collaboration
Binds SpreadJS presence-related capabilities
The spread-sheets-collaboration-client package provides an OT_Type tailored for SpreadJS collaboration. Users can directly use it by referencing it.
Register type:
import * as OT from "@mescius/js-collaboration-ot-client";
import {type} from '@mescius/spread-sheets-collaboration-client';
OT.TypesManager.register(type);Create a document with type:
import {type} from '@mescius/spread-sheets-collaboration-client';
try {
doc.create('Hello', type.uri, {}).then(() => {
console.log('Document created successfully:', doc.data); // "Hello"
});
} catch (err) {
console.error('Creation failed:', err);
}Register type for workbook:
import {type} from '@mescius/spread-sheets-collaboration-client';
const workbook = new GC.Spread.Sheets.Workbook('ss');
workbook.collaboration.registerCollaborationType(type);Workbook currently provides several APIs for Snapshot and ChangeSet, detailed in SpreadJS Sheets Collaboration Add-on.
Doc also offers capabilities for listening to and submitting Ops, detailed in SharedDoc Class .
From the workbook’s perspective, a ChangeSet is equivalent to an Op in Doc, as Doc does not concern itself with specific Ops.
In collaboration, workbook acts as a producer and consumer of Ops, while Doc serves as a entity that can submit and receive Ops. Therefore, binding Workbook and Doc together is necessary.
Active Binding
The core idea aligns with SharedDoc Class - Integration with UI Components.
// Subscribe to the document
doc.subscribe().then(async () => {
// If there’s no type, it means the document hasn’t been created yet; proceed to create it
if (!doc.type) {
// Create the document
await doc.create(workbook.collaboration.toSnapshot()/* default snapshot */, type.uri, {});
console.log('Created successfully:', doc.data);
}
// Refresh the current workbook based on the snapshot
workbook.collaboration.fromSnapshot(doc.data);
// Bind the workbook’s op event; when the workbook has an operation, submit it to doc
workbook.collaboration.onChangeSet((changeSet: IChangeSet) => {
await doc.submitOp(changeSet, { source: doc.connection.id });
});
// Listen for ops received by doc
doc.on('op', (changeSet: IChangeSet, source: unknown) => {
// If the op is sent by itself, no processing is needed
if (source === doc.connection.id) {
return;
}
// Let the workbook apply the generated op
workbook.collaboration.applyChangeSet(changeSet);
});
});Default Bind Method
The spread-sheets-collaboration-client package provides a simple bind method to bind Doc and Workbook, though it does not create the document.
import { bind } from '@mescius/spread-sheets-collaboration-client';
// Request the document
doc.fetch().then(async ()=>{
// If it doesn’t exist, create it
if(!doc.type){
await doc.create(workbook.collaboration.toSnapshot(), type.uri, {});
bind(workbook, doc);
}else{
bind(workbook, doc);
}
});Introduction to Ops: SpreadJS Sheets Add-on - Op
Since Ops describe all modifications to the model, if users want certain data changes (e.g., zoom changes, activeSheet changes) to be excluded from collaboration, there are two approaches:
If you want other clients and the server to not apply a specific Op, filter it by OpType before submitting to Doc:
workbook.collaboration.onChangeSet((changeSet: IChangeSet) => {
const submitOps = changeSet.ops.filter(op => op.type !== OpType.updateZoom);
if (!submitOps || submitOps.length === 0) {
return;
}
doc.submitOp({ ...changeSet, ops: submitOps }, { source: doc.connection.id });
});If you want other clients to not apply an Op but still allow the server to apply it, filter by OpType before workbook applies it:
doc.on('op', (changeSet: IChangeSet, source: unknown) => {
if (source === doc.connection.id) {
return;
}
const needApplyOps = changeSet.ops.filter(op => op.type !== OpType.setActiveSheetId);
if (!needApplyOps || needApplyOps.length === 0) {
return;
};
});The bind method currently includes built-in filters, specifically:
Does not submit: OpType.updateZoom and OpType.updateTopLeftPosition.
Does not apply: OpType.setActiveSheetId, OpType.setTabSelected, and OpType.setStartSheetIndex.
If you do not want these filters, implement your own bind method.
The bindPresence method is provided to implement presence features in SpreadJS. For details, see SpreadJS Sheets Presences
The spread-sheets-collaboration package provides OT_Type.
The spread-sheets-collaboration-client package provides an OT_Type tailored for SpreadJS collaboration. Users can directly use it by referencing it.
Registering Type:
import * as OT from '@mescius/js-collaboration-ot';
import { type } from '@mescius/spread-sheets-collaboration';
OT.TypesManager.register(type);For the remaining server usage and how it aligns with other collaboration workflows, refer to Complete Example of Server Initialization .