[]
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-collaboration
The 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';
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 .