fix(editor): array proxy splice will cause too large yjs update (#12201)

Splice will produce large yjs updates for splice because it will call insert and delete operation for every item in yarray.
<!-- This is an auto-generated comment: release notes by coderabbit.ai -->
## Summary by CodeRabbit

- **New Features**
  - Added support for `splice`, `shift`, and `unshift` methods on reactive arrays, enabling enhanced dynamic array operations with synchronization.
- **Tests**
  - Expanded test coverage for array operations including `push`, `splice`, `shift`, and `unshift`, verifying complete array state after each change.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
This commit is contained in:
Saul-Mirone
2025-05-09 06:19:57 +00:00
parent 2e3b721603
commit 97aa3fc672
2 changed files with 112 additions and 8 deletions
@@ -92,6 +92,69 @@ export class ReactiveYArray extends BaseReactiveYData<
return Reflect.set(target, p, data, receiver);
},
get: (target, p, receiver) => {
if (p === 'splice') {
return (start: number, deleteCount?: number, ...items: unknown[]) => {
const doc = this._ySource.doc;
if (!doc) {
throw new BlockSuiteError(
ErrorCode.ReactiveProxyError,
'YData is not bound to a Y.Doc'
);
}
const count = deleteCount ?? target.length - start;
const yItems = items.map(item => native2Y(item));
this._transact(doc, () => {
this._ySource.delete(start, count);
this._ySource.insert(start, yItems);
});
const result = Array.prototype.splice.apply(target, [
start,
count,
...yItems.map(yItem => createYProxy(yItem, this._options)),
]);
return result;
};
}
if (p === 'shift') {
return () => {
const doc = this._ySource.doc;
if (!doc) {
throw new BlockSuiteError(
ErrorCode.ReactiveProxyError,
'YData is not bound to a Y.Doc'
);
}
if (target.length === 0) {
return undefined;
}
const result = Array.prototype.shift.call(target);
this._transact(doc, () => {
this._ySource.delete(0, 1);
});
return result;
};
}
if (p === 'unshift') {
return (...items: unknown[]) => {
const doc = this._ySource.doc;
if (!doc) {
throw new BlockSuiteError(
ErrorCode.ReactiveProxyError,
'YData is not bound to a Y.Doc'
);
}
const yItems = items.map(item => native2Y(item));
this._transact(doc, () => {
this._ySource.insert(0, yItems);
});
return Array.prototype.unshift.apply(
target,
yItems.map(yItem => createYProxy(yItem, this._options))
);
};
}
return Reflect.get(target, p, receiver);
},
deleteProperty: (target, p): boolean => {