# SpreadJS Sheets Collaboration packages

## Content

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](/spreadjs/docs/v18/spreadjs-collaboration-server/collaboration-framework/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:

```bash
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](/spreadjs/docs/v18/spreadjs-collaboration-server/collaboration-framework/js-collaboration-ot/ottypes) tailored for SpreadJS collaboration. Users can directly use it by referencing it.
Register type:

```typescript
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:

```typescript
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:

```typescript
import {type} from '@mescius/spread-sheets-collaboration-client';

const workbook = new GC.Spread.Sheets.Workbook('ss');
workbook.collaboration.registerCollaborationType(type);
```

### Bind Workbook and Doc

Workbook currently provides several APIs for Snapshot and ChangeSet, detailed in [SpreadJS Sheets Collaboration Add-on](/spreadjs/docs/v18/spreadjs-collaboration-server/spreadjs-sheets-collaboration/spreadjs-sheets-collaboration-add-on).
Doc also offers capabilities for listening to and submitting Ops, detailed in [SharedDoc Class](/spreadjs/docs/v18/spreadjs-collaboration-server/collaboration-framework/js-collaboration-ot/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](/spreadjs/docs/v18/spreadjs-collaboration-server/collaboration-framework/js-collaboration-ot/shareddoc-class).

```typescript
// 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(async (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 ](/spreadjs/api/v18/collaboration/spread-sheets-collaboration-client/README#bind)method to bind Doc and Workbook, though it does not create the document.

```typescript
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](/spreadjs/docs/v18/spreadjs-collaboration-server/spreadjs-sheets-collaboration/spreadjs-sheets-collaboration-add-on)
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:

```typescript
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:

```typescript
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](/spreadjs/docs/v18/spreadjs-collaboration-server/spreadjs-sheets-collaboration/user/presence)

## Server

The `spread-sheets-collaboration` package provides [OT_Type](/spreadjs/docs/v18/spreadjs-collaboration-server/collaboration-framework/js-collaboration-ot/ottypes).

### 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:

```typescript
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](/spreadjs/docs/v18/spreadjs-collaboration-server/collaboration-framework/js-collaboration-ot/initalizing-documentservices) .