[]
        
(Showing Draft Content)

SpreadJS Sheets Collaboration packages

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.

Installation

Only npm installation is provided, with two packages available:

npm install @mescius/spread-sheets-collaboration-client
npm install @mescius/spread-sheets-collaboration

Client

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

OT_Type

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);

Bind Workbook and Doc

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

Filter Certain Ops

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:

Not Submit Certain Ops

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

Not Apply Certain Ops

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

Built-in Filtering in Bind

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.

BindPresence

The bindPresence method is provided to implement presence features in SpreadJS. For details, see SpreadJS Sheets Presences

Server

The spread-sheets-collaboration package provides OT_Type.

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);

Other Server Logic

For the remaining server usage and how it aligns with other collaboration workflows, refer to Complete Example of Server Initialization .