vyjs
v2.0.5
Published
This project provides utilities for synchronizing Yjs documents with plain objects, applying diffs/deltas, and handling nested structures, arrays, and text types.
Downloads
32
Maintainers
Readme
vyjs - Vanilla Yjs Integration
This project provides utilities for synchronizing Yjs documents with plain objects, applying diffs/deltas, and handling nested structures, arrays, and text types. It aims to simplify the integration of Yjs with React applications by offering a straightforward and vanilla approach.
Table of Contents
Introduction
vyjs (Vanilla Yjs) is a utility library designed to simplify the integration of Yjs with React and other JavaScript frameworks. It provides a minimalistic approach to synchronize Yjs documents with plain JavaScript objects, making it easier to build real-time collaborative applications without the overhead of complex abstractions.
Features
- Vanilla Integration: Offers a straightforward way to use Yjs without additional abstractions.
- React Compatibility: Simplifies the integration of Yjs with React applications.
- Synchronization: Automatically synchronize Yjs documents with plain JavaScript objects.
- Difference Application: Apply the difference between two JSON objects to Yjs types.
- Nested Structures: Support for nested maps, arrays, and text types within Yjs documents.
- Conflict Resolution: Handle concurrent changes with Yjs's built-in conflict resolution mechanisms.
- Comprehensive Tests: A suite of tests demonstrating various synchronization scenarios.
Installation
Install vyjs via npm:
npm install vyjs
Usage
Synchronizing Yjs Documents with Plain Objects
Import the necessary modules:
import * as Y from 'yjs';
import { getYjsEventsHandler } from 'vyjs';
const ydoc = new Y.Doc();
const yMap = ydoc.getMap('rootMap');
let currentValue = {};
const callback = (updatedValue) => {
currentValue = updatedValue;
console.log('Updated Value:', currentValue);
};
const eventsHandler = getYjsEventsHandler(currentValue, callback);
yMap.observeDeep(eventsHandler);
// Now, any changes made to yMap will be reflected in currentValue via the callback:
ydoc.transact(() => {
yMap.set('number', 42);
yMap.set('text', new Y.Text('Hello, Yjs!'));
});
Integrating with React
vyjs makes it easier to integrate Yjs with React by keeping your component state in sync with Yjs documents.
import React, { useEffect, useState } from 'react';
import * as Y from 'yjs';
import { getYjsEventsHandler } from 'vyjs';
const ydoc = new Y.Doc();
const yMap = ydoc.getMap('rootMap');
function App() {
const [state, setState] = useState({});
useEffect(() => {
let currentValue = {};
const callback = (updatedValue) => {
setState(updatedValue);
};
const eventsHandler = getYjsEventsHandler(currentValue, callback);
yMap.observeDeep(eventsHandler);
return () => {
yMap.unobserveDeep(eventsHandler);
};
}, []);
return (
<div>
<h1>Yjs and React Integration</h1>
<pre>{JSON.stringify(state, null, 2)}</pre>
</div>
);
}
export default App;
With this setup, any changes to the Yjs document will automatically update the React component's state, ensuring seamless synchronization between the collaborative data and your UI.
Applying Differences Between JSON Objects to Yjs Types
Import the applyJsonDiffToYjs
function:
import { applyJsonDiffToYjs } from 'vyjs';
import * as Y from 'yjs';
const ydoc = new Y.Doc();
const yMap = ydoc.getMap('map');
const oldValue = {
number: 42,
text: 'Hello, Yjs!',
};
const newValue = {
number: 100,
text: 'Hi',
status: true,
};
// Initialize yMap with oldValue
Object.keys(oldValue).forEach((key) => {
yMap.set(key, oldValue[key]);
});
// Apply the difference between oldValue and newValue to yMap
applyJsonDiffToYjs(oldValue, newValue, yMap);
console.log(yMap.get('number')); // Output: 100
console.log(yMap.get('status')); // Output: true
console.log(yMap.get('text').toString()); // Output: 'Hi'
Running Tests
npm test
API Reference
getYjsEventsHandler(currentValue, callback)
Returns an event handler function that synchronizes changes from a Yjs document to a plain JavaScript object.
- Parameters:
currentValue
(Object): The current value of the plain JavaScript object.callback
(Function): The function to call with the updated value.
- Returns:
- (Function): An event handler to be used with
yMap.observeDeep()
.
- (Function): An event handler to be used with
Usage:
const eventsHandler = getYjsEventsHandler(currentValue, callback);
yMap.observeDeep(eventsHandler);
applyJsonDiffToYjs(oldValue, newValue, yValue, yValueParent, yValueKey)
Applies the difference between two JSON objects to a Yjs type.
- Parameters:
oldValue
(any): The original JSON value.newValue
(any): The new JSON value.yValue
(Y.Map | Y.Array | Y.Text): The Yjs type to apply the changes to.yValueParent
(Y.Map | Y.Array | undefined): The parent of the Yjs type.yValueKey
(string | number | undefined): The key or index in the parent whereyValue
is located.
- Returns:
void
Usage:
applyJsonDiffToYjs(oldValue, newValue, yValue, yValueParent, yValueKey);
jsonToYType(object)
Converts a JSON object to a Yjs type.
- Parameters:
object
(any): The JSON object to convert.
- Returns:
- Y.Map | Y.Array | Y.Text | primitive value
Usage:
const yType = jsonToYType(jsonObject);
Examples
Nested Structures Synchronization
Synchronize nested maps and arrays between a Yjs document and a plain object.
ydoc.transact(() => {
const nestedMap = new Y.Map();
nestedMap.set('key1', 'value1');
yMap.set('nested', nestedMap);
});
console.log(currentValue);
// Output:
// {
// nested: {
// key1: 'value1',
// },
// }
// Update nested map
ydoc.transact(() => {
const nestedMap = yMap.get('nested');
nestedMap.set('key2', 'value2');
});
console.log(currentValue);
// Output:
// {
// nested: {
// key1: 'value1',
// key2: 'value2',
// },
// }
License
This project is licensed under the MIT License. See the LICENSE file for details.
Acknowledgments
- Yjs: A powerful CRDT implementation for building collaborative applications.