mirror of
https://github.com/yjs/yjs.git
synced 2026-02-24 04:01:14 +01:00
simplifying type system
This commit is contained in:
55
package-lock.json
generated
55
package-lock.json
generated
@@ -9,7 +9,7 @@
|
||||
"version": "14.0.0-19",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"lib0": "^0.2.115"
|
||||
"lib0": "file:../lib0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "^22.14.1",
|
||||
@@ -29,6 +29,28 @@
|
||||
"url": "https://github.com/sponsors/dmonad"
|
||||
}
|
||||
},
|
||||
"../lib0": {
|
||||
"version": "0.2.115",
|
||||
"license": "MIT",
|
||||
"bin": {
|
||||
"0ecdsa-generate-keypair": "src/bin/0ecdsa-generate-keypair.js",
|
||||
"0gentesthtml": "src/bin/gentesthtml.js",
|
||||
"0serve": "src/bin/0serve.js"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "^24.0.14",
|
||||
"c8": "^10.1.3",
|
||||
"standard": "^17.1.0",
|
||||
"typescript": "^5.9.3"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=16"
|
||||
},
|
||||
"funding": {
|
||||
"type": "GitHub Sponsors ❤",
|
||||
"url": "https://github.com/sponsors/dmonad"
|
||||
}
|
||||
},
|
||||
"node_modules/@eslint-community/eslint-utils": {
|
||||
"version": "4.9.0",
|
||||
"resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.9.0.tgz",
|
||||
@@ -3143,16 +3165,6 @@
|
||||
"dev": true,
|
||||
"license": "ISC"
|
||||
},
|
||||
"node_modules/isomorphic.js": {
|
||||
"version": "0.2.5",
|
||||
"resolved": "https://registry.npmjs.org/isomorphic.js/-/isomorphic.js-0.2.5.tgz",
|
||||
"integrity": "sha512-PIeMbHqMt4DnUP3MA/Flc0HElYjMXArsw1qwJZcm9sqR8mq3l8NYizFMty0pWwE/tzIGH3EKK5+jes5mAr85yw==",
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"type": "GitHub Sponsors ❤",
|
||||
"url": "https://github.com/sponsors/dmonad"
|
||||
}
|
||||
},
|
||||
"node_modules/iterator.prototype": {
|
||||
"version": "1.1.5",
|
||||
"resolved": "https://registry.npmjs.org/iterator.prototype/-/iterator.prototype-1.1.5.tgz",
|
||||
@@ -3333,25 +3345,8 @@
|
||||
}
|
||||
},
|
||||
"node_modules/lib0": {
|
||||
"version": "0.2.115",
|
||||
"resolved": "https://registry.npmjs.org/lib0/-/lib0-0.2.115.tgz",
|
||||
"integrity": "sha512-noaW4yNp6hCjOgDnWWxW0vGXE3kZQI5Kqiwz+jIWXavI9J9WyfJ9zjsbQlQlgjIbHBrvlA/x3TSIXBUJj+0L6g==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"isomorphic.js": "^0.2.4"
|
||||
},
|
||||
"bin": {
|
||||
"0ecdsa-generate-keypair": "bin/0ecdsa-generate-keypair.js",
|
||||
"0gentesthtml": "bin/gentesthtml.js",
|
||||
"0serve": "bin/0serve.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=16"
|
||||
},
|
||||
"funding": {
|
||||
"type": "GitHub Sponsors ❤",
|
||||
"url": "https://github.com/sponsors/dmonad"
|
||||
}
|
||||
"resolved": "../lib0",
|
||||
"link": true
|
||||
},
|
||||
"node_modules/linkify-it": {
|
||||
"version": "5.0.0",
|
||||
|
||||
@@ -73,7 +73,7 @@
|
||||
},
|
||||
"homepage": "https://docs.yjs.dev",
|
||||
"dependencies": {
|
||||
"lib0": "^0.2.115"
|
||||
"lib0": "file:../lib0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "^22.14.1",
|
||||
|
||||
@@ -275,8 +275,7 @@ export const callTypeObservers = (type, transaction, event) => {
|
||||
|
||||
/**
|
||||
* Abstract Yjs Type class
|
||||
* @template {delta.Delta<any,any,any,any,any>} [EventDelta=any]
|
||||
* @template {AbstractType<any,any>} [Self=any]
|
||||
* @template {delta.DeltaConf} [DConf=any]
|
||||
*/
|
||||
export class AbstractType {
|
||||
constructor () {
|
||||
@@ -299,7 +298,7 @@ export class AbstractType {
|
||||
this._length = 0
|
||||
/**
|
||||
* Event handlers
|
||||
* @type {EventHandler<YEvent<Self>,Transaction>}
|
||||
* @type {EventHandler<YEvent<DConf>,Transaction>}
|
||||
*/
|
||||
this._eH = createEventHandler()
|
||||
/**
|
||||
@@ -312,14 +311,15 @@ export class AbstractType {
|
||||
*/
|
||||
this._searchMarker = null
|
||||
/**
|
||||
* @type {EventDelta?}
|
||||
* @type {delta.DeltaBuilder<DConf>}
|
||||
* @private
|
||||
*/
|
||||
this._prelim = null
|
||||
this._content = /** @type {delta.DeltaBuilderAny} */ (delta.create())
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a fresh delta that can be used to change this YType.
|
||||
* @type {EventDelta}
|
||||
* @type {delta.DeltaBuilder<DConf>}
|
||||
*/
|
||||
get change () {
|
||||
return /** @type {any} */ (delta.create())
|
||||
@@ -352,7 +352,7 @@ export class AbstractType {
|
||||
}
|
||||
|
||||
/**
|
||||
* @return {Self}
|
||||
* @return {this}
|
||||
*/
|
||||
_copy () {
|
||||
// @ts-ignore
|
||||
@@ -364,7 +364,7 @@ export class AbstractType {
|
||||
*
|
||||
* Note that the content is only readable _after_ it has been included somewhere in the Ydoc.
|
||||
*
|
||||
* @return {Self}
|
||||
* @return {this}
|
||||
*/
|
||||
clone () {
|
||||
// @todo remove this method from othern types by doing `_copy().apply(this.getContent())`
|
||||
@@ -405,7 +405,7 @@ export class AbstractType {
|
||||
/**
|
||||
* Observe all events that are created on this type.
|
||||
*
|
||||
* @template {(target: YEvent<Self>, tr: Transaction) => void} F
|
||||
* @template {(target: YEvent<DConf>, tr: Transaction) => void} F
|
||||
* @param {F} f Observer function
|
||||
* @return {F}
|
||||
*/
|
||||
@@ -429,7 +429,7 @@ export class AbstractType {
|
||||
/**
|
||||
* Unregister an observer function.
|
||||
*
|
||||
* @param {(type:YEvent<Self>,tr:Transaction)=>void} f Observer function
|
||||
* @param {(type:YEvent<DConf>,tr:Transaction)=>void} f Observer function
|
||||
*/
|
||||
unobserve (f) {
|
||||
removeEventHandlerListener(this._eH, f)
|
||||
@@ -467,16 +467,16 @@ export class AbstractType {
|
||||
* @param {import('../utils/IdSet.js').IdSet?} [opts.deletedItems] - used for computing prevItem in attributes
|
||||
* @param {Map<import('../utils/types.js').YType,Set<string|null>>|null} [opts.modified] - set of types that should be rendered as modified children
|
||||
* @param {Deep} [opts.deep] - render child types as delta
|
||||
* @return {Deep extends true ? ToDeepEventDelta<EventDelta> : EventDelta} The Delta representation of this type.
|
||||
* @return {Deep extends true ? delta.Delta<DeltaConfTypesToDelta<DConf>> : delta.Delta<DConf>} The Delta representation of this type.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
getContent (am = noAttributionsManager, opts = {}) {
|
||||
const { itemsToRender = null, retainInserts = false, retainDeletes = false, deletedItems = null, modified = null, deep = false } = opts
|
||||
const renderAttrs = modified?.get(this) || null
|
||||
const renderChildren = (modified == null || opts.modified.get(this)?.has(null))
|
||||
const renderChildren = !!(modified == null || modified.get(this)?.has(null))
|
||||
/**
|
||||
* @type {EventDelta extends delta.Delta<infer N,infer Attrs,infer Children,infer Text,any> ? delta.DeltaBuilder<N,Attrs,Children,Text,any> : never}
|
||||
* @type {delta.DeltaBuilderAny}
|
||||
*/
|
||||
const d = /** @type {any} */ (delta.create(/** @type {any} */ (this).nodeName || null))
|
||||
const optsAll = modified == null ? opts : object.assign({}, opts, { modified: null })
|
||||
@@ -693,7 +693,7 @@ export class AbstractType {
|
||||
* attributions.
|
||||
*
|
||||
* @param {AbstractAttributionManager} am
|
||||
* @return {ToDeepEventDelta<EventDelta>}
|
||||
* @return {delta.Delta<DeltaConfTypesToDelta<DConf>>}
|
||||
*/
|
||||
getContentDeep (am = noAttributionsManager) {
|
||||
return /** @type {any} */ (this.getContent(am, { deep: true }))
|
||||
@@ -702,7 +702,7 @@ export class AbstractType {
|
||||
/**
|
||||
* Apply a {@link Delta} on this shared type.
|
||||
*
|
||||
* @param {delta.Delta<any,any,any,any,any>} d The changes to apply on this element.
|
||||
* @param {delta.DeltaAny} d The changes to apply on this element.
|
||||
* @param {AbstractAttributionManager} am
|
||||
*
|
||||
* @public
|
||||
@@ -747,12 +747,12 @@ export class AbstractType {
|
||||
}
|
||||
}
|
||||
for (const op of d.attrs) {
|
||||
if (delta.$insertOp.check(op)) {
|
||||
typeMapSet(transaction, /** @type {any} */ (this), op.key, op.value)
|
||||
if (delta.$setAttrOp.check(op)) {
|
||||
typeMapSet(transaction, /** @type {any} */ (this), /** @type {any} */ (op.key), op.value)
|
||||
} else if (delta.$deleteOp.check(op)) {
|
||||
typeMapDelete(transaction, /** @type {any} */ (this), op.key)
|
||||
typeMapDelete(transaction, /** @type {any} */ (this), /** @type {any} */ (op.key))
|
||||
} else {
|
||||
const sub = typeMapGet(/** @type {any} */ (this), op.key)
|
||||
const sub = typeMapGet(/** @type {any} */ (this), /** @type {any} */ (op.key))
|
||||
if (!(sub instanceof AbstractType)) {
|
||||
error.unexpectedCase()
|
||||
}
|
||||
@@ -772,21 +772,14 @@ export class AbstractType {
|
||||
export const equalAttrs = (a, b) => a === b || (typeof a === 'object' && typeof b === 'object' && a && b && object.equalFlat(a, b))
|
||||
|
||||
/**
|
||||
* @template {delta.Delta<any,any,any,any,any>} D
|
||||
* @typedef {D extends delta.Delta<infer N,infer Attrs,infer Cs,infer Text,any>
|
||||
* ? delta.Delta<
|
||||
* N,
|
||||
* { [K in keyof Attrs]: TypeToDelta<Attrs[K]> },
|
||||
* TypeToDelta<Cs>,
|
||||
* Text
|
||||
* >
|
||||
* : D
|
||||
* } ToDeepEventDelta
|
||||
*/
|
||||
|
||||
/**
|
||||
* @template {any} T
|
||||
* @typedef {(Extract<T,AbstractType<any>> extends AbstractType<infer D> ? (unknown extends D ? never : ToDeepEventDelta<D>) : never) | Exclude<T,AbstractType<any>>} TypeToDelta
|
||||
* @template {delta.DeltaConf} DConf
|
||||
* @typedef {delta.DeltaConfOverwrite<DConf, {
|
||||
* attrs: { [K in keyof delta.DeltaConfGetAttrs<DConf>]: delta.DeltaConfGetAttrs<DConf>[K] },
|
||||
* children: (Extract<delta.DeltaConfGetChildren<DConf>,AbstractType<any>> extends AbstractType<infer SubDConf> ? (
|
||||
* unknown extends SubDConf ? never : (delta.Delta<DeltaConfTypesToDelta<SubDConf>>)
|
||||
* ) : never) | Exclude<delta.DeltaConfGetChildren<DConf>,AbstractType<any>>
|
||||
* }>
|
||||
* } DeltaConfTypesToDelta
|
||||
*/
|
||||
|
||||
/**
|
||||
@@ -1309,7 +1302,7 @@ export const typeMapGetAll = (parent) => {
|
||||
* Note that deleted content that was not deleted in prevYdoc is rendered as an insertion with the
|
||||
* attribution `{ isDeleted: true, .. }`.
|
||||
*
|
||||
* @template {delta.DeltaBuilder<any,any,any,any>} TypeDelta
|
||||
* @template {delta.DeltaBuilderAny} TypeDelta
|
||||
* @param {TypeDelta} d
|
||||
* @param {YType_} parent
|
||||
* @param {Set<string|null>?} attrsToRender
|
||||
@@ -1341,10 +1334,10 @@ export const typeMapGetDelta = (d, parent, attrsToRender, am, deep, modified, de
|
||||
let c = array.last(content.getContent())
|
||||
if (deleted) {
|
||||
if (itemsToRender == null || itemsToRender.hasId(item.lastId)) {
|
||||
d.unset(key, attribution, c)
|
||||
d.deleteAttr(key, attribution, c)
|
||||
}
|
||||
} else if (deep && c instanceof AbstractType && modified?.has(c)) {
|
||||
d.update(key, c.getContent(am, opts))
|
||||
d.modifyAttr(key, c.getContent(am, opts))
|
||||
} else {
|
||||
// find prev content
|
||||
let prevContentItem = item
|
||||
@@ -1356,7 +1349,7 @@ export const typeMapGetDelta = (d, parent, attrsToRender, am, deep, modified, de
|
||||
if (deep && c instanceof AbstractType) {
|
||||
c = /** @type {any} */(c).getContent(am, optsAll)
|
||||
}
|
||||
d.set(key, c, attribution, prevValue)
|
||||
d.setAttr(key, c, attribution, prevValue)
|
||||
}
|
||||
}
|
||||
if (attrsToRender == null) {
|
||||
|
||||
270
src/types/Type.js
Normal file
270
src/types/Type.js
Normal file
@@ -0,0 +1,270 @@
|
||||
/**
|
||||
* @module YArray
|
||||
*/
|
||||
|
||||
import {
|
||||
AbstractType,
|
||||
typeListGet,
|
||||
typeListToArray,
|
||||
typeListForEach,
|
||||
typeListCreateIterator,
|
||||
typeListInsertGenerics,
|
||||
typeListPushGenerics,
|
||||
typeListDelete,
|
||||
typeListMap,
|
||||
YArrayRefID,
|
||||
transact,
|
||||
warnPrematureAccess,
|
||||
typeListSlice,
|
||||
noAttributionsManager,
|
||||
AbstractAttributionManager, ArraySearchMarker, UpdateDecoderV1, UpdateDecoderV2, UpdateEncoderV1, UpdateEncoderV2, Doc, Transaction, Item // eslint-disable-line
|
||||
} from '../internals.js'
|
||||
|
||||
import * as delta from 'lib0/delta'
|
||||
|
||||
/**
|
||||
* A shared Array implementation.
|
||||
* @template {import('../utils/types.js').YValue} T
|
||||
* @extends {AbstractType<delta.Delta<T>,YArray<T>>}
|
||||
* @implements {Iterable<T>}
|
||||
*/
|
||||
// @todo remove this
|
||||
// @ts-ignore
|
||||
export class YType extends AbstractType {
|
||||
constructor () {
|
||||
super()
|
||||
/**
|
||||
* @type {Array<any>?}
|
||||
* @private
|
||||
*/
|
||||
this._prelimContent = []
|
||||
/**
|
||||
* @type {Array<ArraySearchMarker>}
|
||||
*/
|
||||
this._searchMarker = []
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct a new YArray containing the specified items.
|
||||
* @template {import('../utils/types.js').YValue} T
|
||||
* @param {Array<T>} items
|
||||
* @return {YArray<T>}
|
||||
*/
|
||||
static from (items) {
|
||||
/**
|
||||
* @type {YArray<T>}
|
||||
*/
|
||||
const a = new YArray()
|
||||
a.push(items)
|
||||
return a
|
||||
}
|
||||
|
||||
/**
|
||||
* Integrate this type into the Yjs instance.
|
||||
*
|
||||
* * Save this struct in the os
|
||||
* * This type is sent to other client
|
||||
* * Observer functions are fired
|
||||
*
|
||||
* @param {Doc} y The Yjs instance
|
||||
* @param {Item?} item
|
||||
*/
|
||||
_integrate (y, item) {
|
||||
super._integrate(y, item)
|
||||
this.insert(0, /** @type {Array<any>} */ (this._prelimContent))
|
||||
this._prelimContent = null
|
||||
}
|
||||
|
||||
/**
|
||||
* Makes a copy of this data type that can be included somewhere else.
|
||||
*
|
||||
* Note that the content is only readable _after_ it has been included somewhere in the Ydoc.
|
||||
*
|
||||
* @return {YArray<T>}
|
||||
*/
|
||||
clone () {
|
||||
/**
|
||||
* @type {this}
|
||||
*/
|
||||
const arr = /** @type {this} */ (new YArray())
|
||||
arr.insert(0, this.toArray().map(el =>
|
||||
// @ts-ignore
|
||||
el instanceof AbstractType ? /** @type {any} */ (el.clone()) : el
|
||||
))
|
||||
return arr
|
||||
}
|
||||
|
||||
get length () {
|
||||
this.doc ?? warnPrematureAccess()
|
||||
return this._length
|
||||
}
|
||||
|
||||
/**
|
||||
* Inserts new content at an index.
|
||||
*
|
||||
* Important: This function expects an array of content. Not just a content
|
||||
* object. The reason for this "weirdness" is that inserting several elements
|
||||
* is very efficient when it is done as a single operation.
|
||||
*
|
||||
* @example
|
||||
* // Insert character 'a' at position 0
|
||||
* yarray.insert(0, ['a'])
|
||||
* // Insert numbers 1, 2 at position 1
|
||||
* yarray.insert(1, [1, 2])
|
||||
*
|
||||
* @param {number} index The index to insert content at.
|
||||
* @param {Array<T>} content The array of content
|
||||
*/
|
||||
insert (index, content) {
|
||||
if (this.doc !== null) {
|
||||
transact(this.doc, transaction => {
|
||||
typeListInsertGenerics(transaction, this, index, /** @type {any} */ (content))
|
||||
})
|
||||
} else {
|
||||
/** @type {Array<any>} */ (this._prelimContent).splice(index, 0, ...content)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Appends content to this YArray.
|
||||
*
|
||||
* @param {Array<T>} content Array of content to append.
|
||||
*
|
||||
* @todo Use the following implementation in all types.
|
||||
*/
|
||||
push (content) {
|
||||
if (this.doc !== null) {
|
||||
transact(this.doc, transaction => {
|
||||
typeListPushGenerics(transaction, this, /** @type {any} */ (content))
|
||||
})
|
||||
} else {
|
||||
/** @type {Array<any>} */ (this._prelimContent).push(...content)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepends content to this YArray.
|
||||
*
|
||||
* @param {Array<T>} content Array of content to prepend.
|
||||
*/
|
||||
unshift (content) {
|
||||
this.insert(0, content)
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes elements starting from an index.
|
||||
*
|
||||
* @param {number} index Index at which to start deleting elements
|
||||
* @param {number} length The number of elements to remove. Defaults to 1.
|
||||
*/
|
||||
delete (index, length = 1) {
|
||||
if (this.doc !== null) {
|
||||
transact(this.doc, transaction => {
|
||||
typeListDelete(transaction, this, index, length)
|
||||
})
|
||||
} else {
|
||||
/** @type {Array<any>} */ (this._prelimContent).splice(index, length)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the i-th element from a YArray.
|
||||
*
|
||||
* @param {number} index The index of the element to return from the YArray
|
||||
* @return {T}
|
||||
*/
|
||||
get (index) {
|
||||
return typeListGet(this, index)
|
||||
}
|
||||
|
||||
/**
|
||||
* Transforms this YArray to a JavaScript Array.
|
||||
*
|
||||
* @return {Array<T>}
|
||||
*/
|
||||
toArray () {
|
||||
return typeListToArray(this)
|
||||
}
|
||||
|
||||
/**
|
||||
* Render the difference to another ydoc (which can be empty) and highlight the differences with
|
||||
* attributions.
|
||||
*
|
||||
* Note that deleted content that was not deleted in prevYdoc is rendered as an insertion with the
|
||||
* attribution `{ isDeleted: true, .. }`.
|
||||
*
|
||||
* @param {AbstractAttributionManager} am
|
||||
* @return {delta.ArrayDelta<import('./AbstractType.js').TypeToDelta<T>>} The Delta representation of this type.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
getContentDeep (am = noAttributionsManager) {
|
||||
return super.getContentDeep(am)
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a portion of this YArray into a JavaScript Array selected
|
||||
* from start to end (end not included).
|
||||
*
|
||||
* @param {number} [start]
|
||||
* @param {number} [end]
|
||||
* @return {Array<T>}
|
||||
*/
|
||||
slice (start = 0, end = this.length) {
|
||||
return typeListSlice(this, start, end)
|
||||
}
|
||||
|
||||
/**
|
||||
* Transforms this Shared Type to a JSON object.
|
||||
*
|
||||
* @return {Array<any>}
|
||||
*/
|
||||
toJSON () {
|
||||
return this.map(c => c instanceof AbstractType ? c.toJSON() : c)
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an Array with the result of calling a provided function on every
|
||||
* element of this YArray.
|
||||
*
|
||||
* @template M
|
||||
* @param {function(T,number,YArray<T>):M} f Function that produces an element of the new Array
|
||||
* @return {Array<M>} A new array with each element being the result of the
|
||||
* callback function
|
||||
*/
|
||||
map (f) {
|
||||
return typeListMap(this, /** @type {any} */ (f))
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes a provided function once on every element of this YArray.
|
||||
*
|
||||
* @param {function(T,number,YArray<T>):void} f A function to execute on every element of this YArray.
|
||||
*/
|
||||
forEach (f) {
|
||||
typeListForEach(this, f)
|
||||
}
|
||||
|
||||
/**
|
||||
* @return {IterableIterator<T>}
|
||||
*/
|
||||
[Symbol.iterator] () {
|
||||
return typeListCreateIterator(this)
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {UpdateEncoderV1 | UpdateEncoderV2} encoder
|
||||
*/
|
||||
_write (encoder) {
|
||||
encoder.writeTypeRef(YArrayRefID)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {UpdateDecoderV1 | UpdateDecoderV2} _decoder
|
||||
* @return {import('../utils/types.js').YType}
|
||||
*
|
||||
* @private
|
||||
* @function
|
||||
*/
|
||||
export const readYArray = _decoder => new YArray()
|
||||
@@ -13,28 +13,24 @@ import * as delta from 'lib0/delta' // eslint-disable-line
|
||||
import * as set from 'lib0/set'
|
||||
|
||||
/**
|
||||
* @typedef {import('./types.js').YType} _YType
|
||||
*/
|
||||
|
||||
/**
|
||||
* @template {AbstractType<any,any>} Target
|
||||
* @template {delta.DeltaConf} DConf
|
||||
* YEvent describes the changes on a YType.
|
||||
*/
|
||||
export class YEvent {
|
||||
/**
|
||||
* @param {Target} target The changed type.
|
||||
* @param {AbstractType<DConf>} target The changed type.
|
||||
* @param {Transaction} transaction
|
||||
* @param {Set<any>?} subs The keys that changed
|
||||
*/
|
||||
constructor (target, transaction, subs) {
|
||||
/**
|
||||
* The type on which this event was created on.
|
||||
* @type {Target}
|
||||
* @type {AbstractType<DConf>}
|
||||
*/
|
||||
this.target = target
|
||||
/**
|
||||
* The current target on which the observe callback is called.
|
||||
* @type {_YType}
|
||||
* @type {AbstractType<any>}
|
||||
*/
|
||||
this.currentTarget = target
|
||||
/**
|
||||
@@ -43,11 +39,11 @@ export class YEvent {
|
||||
*/
|
||||
this.transaction = transaction
|
||||
/**
|
||||
* @type {(Target extends AbstractType<infer D,any> ? D : delta.Delta<any,any,any,any,any>)|null}
|
||||
* @type {delta.Delta<DConf>|null}
|
||||
*/
|
||||
this._delta = null
|
||||
/**
|
||||
* @type {(Target extends AbstractType<infer D,any> ? import('../internals.js').ToDeepEventDelta<D> : delta.Delta<any,any,any,any,any>)|null}
|
||||
* @type {import('../types/AbstractType.js').DeltaConfTypesToDeltaDelta<DConf>|null}
|
||||
*/
|
||||
this._deltaDeep = null
|
||||
/**
|
||||
@@ -120,7 +116,7 @@ export class YEvent {
|
||||
* @param {AbstractAttributionManager} am
|
||||
* @param {object} [opts]
|
||||
* @param {Deep} [opts.deep]
|
||||
* @return {Target extends AbstractType<infer D,any> ? (Deep extends true ? import('../internals.js').ToDeepEventDelta<D> : D) : delta.Delta<any,any,any,any>} The Delta representation of this type.
|
||||
* @return {Deep extends true ? delta.Delta<import('../internals.js').DeltaConfTypesToDelta<DConf>> : delta.Delta<DConf>} The Delta representation of this type.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
@@ -159,7 +155,7 @@ export class YEvent {
|
||||
* Compute the changes in the delta format.
|
||||
* A {@link https://quilljs.com/docs/delta/|Quill Delta}) that represents the changes on the document.
|
||||
*
|
||||
* @type {Target extends AbstractType<infer D,any> ? D : delta.Delta<any,any,any,any,any>} The Delta representation of this type.
|
||||
* @type {delta.Delta<DConf>} The Delta representation of this type.
|
||||
* @public
|
||||
*/
|
||||
get delta () {
|
||||
@@ -170,11 +166,11 @@ export class YEvent {
|
||||
* Compute the changes in the delta format.
|
||||
* A {@link https://quilljs.com/docs/delta/|Quill Delta}) that represents the changes on the document.
|
||||
*
|
||||
* @type {Target extends AbstractType<infer D,any> ? D : delta.Delta<any,any,any,any,any>} The Delta representation of this type.
|
||||
* @type {import('../internals.js').DeltaConfTypesToDeltaDelta<DConf>} The Delta representation of this type.
|
||||
* @public
|
||||
*/
|
||||
get deltaDeep () {
|
||||
return /** @type {any} */ (this._deltaDeep ?? (this._deltaDeep = this.getDelta(noAttributionsManager, { deep: true })))
|
||||
return /** @type {any} */ (this._deltaDeep ?? (this._deltaDeep = /** @type {any} */ (this.getDelta(noAttributionsManager, { deep: true }))))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -188,8 +184,8 @@ export class YEvent {
|
||||
* console.log(path) // might look like => [2, 'key1']
|
||||
* child === type.get(path[0]).get(path[1])
|
||||
*
|
||||
* @param {_YType} parent
|
||||
* @param {_YType} child target
|
||||
* @param {AbstractType} parent
|
||||
* @param {AbstractType} child target
|
||||
* @param {AbstractAttributionManager} am
|
||||
* @return {Array<string|number>} Path to the target
|
||||
*
|
||||
@@ -209,7 +205,7 @@ export const getPathTo = (parent, child, am = noAttributionsManager) => {
|
||||
const apos = /** @type {AbsolutePosition} */ (createAbsolutePositionFromRelativePosition(createRelativePosition(parent, child._item.id), doc, false, am))
|
||||
path.unshift(apos.index)
|
||||
}
|
||||
child = /** @type {_YType} */ (child._item.parent)
|
||||
child = /** @type {AbstractType} */ (child._item.parent)
|
||||
}
|
||||
return path
|
||||
}
|
||||
|
||||
@@ -46,7 +46,7 @@ export const diffDocsToDelta = (v1, v2, { am = createAttributionManagerFromDiff(
|
||||
const shareDelta = type.getContent(am, {
|
||||
itemsToRender, retainDeletes: true, deletedItems: deletesOnly, modified: changedTypes, deep: true
|
||||
})
|
||||
d.update(typename, shareDelta)
|
||||
d.modifyAttr(typename, shareDelta)
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
@@ -40,11 +40,11 @@ export const testAttributedEvents = _tc => {
|
||||
})
|
||||
const am = Y.createAttributionManagerFromDiff(v1, ydoc)
|
||||
const c1 = ytext.getContent(am)
|
||||
t.compare(c1, delta.text().insert('hello ').insert('world', null, { delete: [] }))
|
||||
t.compare(c1, delta.create().insert('hello ').insert('world', null, { delete: [] }))
|
||||
let calledObserver = false
|
||||
ytext.observe(event => {
|
||||
const d = event.getDelta(am)
|
||||
t.compare(d, delta.text().retain(11).insert('!', null, { insert: [] }))
|
||||
t.compare(d, delta.create().retain(11).insert('!', null, { insert: [] }))
|
||||
calledObserver = true
|
||||
})
|
||||
ytext.insert(11, '!')
|
||||
@@ -64,8 +64,8 @@ export const testInsertionsMindingAttributedContent = _tc => {
|
||||
})
|
||||
const am = Y.createAttributionManagerFromDiff(v1, ydoc)
|
||||
const c1 = ytext.getContent(am)
|
||||
t.compare(c1, delta.text().insert('hello ').insert('world', null, { delete: [] }))
|
||||
ytext.applyDelta(delta.text().retain(11).insert('content'), am)
|
||||
t.compare(c1, delta.create().insert('hello ').insert('world', null, { delete: [] }))
|
||||
ytext.applyDelta(delta.create().retain(11).insert('content'), am)
|
||||
t.assert(ytext.toString() === 'hello content')
|
||||
}
|
||||
|
||||
@@ -82,8 +82,8 @@ export const testInsertionsIntoAttributedContent = _tc => {
|
||||
})
|
||||
const am = Y.createAttributionManagerFromDiff(v1, ydoc)
|
||||
const c1 = ytext.getContent(am)
|
||||
t.compare(c1, delta.text().insert('hello ').insert('word', null, { insert: [] }))
|
||||
ytext.applyDelta(delta.text().retain(9).insert('l'), am)
|
||||
t.compare(c1, delta.create().insert('hello ').insert('word', null, { insert: [] }))
|
||||
ytext.applyDelta(delta.create().retain(9).insert('l'), am)
|
||||
t.assert(ytext.toString() === 'hello world')
|
||||
}
|
||||
|
||||
@@ -102,9 +102,9 @@ export const testYdocDiff = () => {
|
||||
const d = Y.diffDocsToDelta(ydocStart, ydocUpdated)
|
||||
console.log('calculated diff', d.toJSON())
|
||||
t.compare(d, delta.create()
|
||||
.update('text', delta.create().retain(5).insert(' world', null, { insert: [] }))
|
||||
.update('array', delta.create().retain(1).insert(['x'], null, { insert: [] }))
|
||||
.update('map', delta.create().set('newk', 42, { insert: [] }).update('nested', delta.create().insert([1], null, { insert: [] })))
|
||||
.modifyAttr('text', delta.create().retain(5).insert(' world', null, { insert: [] }))
|
||||
.modifyAttr('array', delta.create().retain(1).insert(['x'], null, { insert: [] }))
|
||||
.modifyAttr('map', delta.create().setAttr('newk', 42, { insert: [] }).modifyAttr('nested', delta.create().insert([1], null, { insert: [] })))
|
||||
)
|
||||
}
|
||||
|
||||
@@ -113,21 +113,21 @@ export const testChildListContent = () => {
|
||||
const ydocUpdated = Y.cloneDoc(ydocStart)
|
||||
const yf = new Y.XmlElement('test')
|
||||
let calledEvent = 0
|
||||
yf.applyDelta(delta.create().insert('test content').set('k', 'v'))
|
||||
yf.applyDelta(delta.create().insert('test content').setAttr('k', 'v'))
|
||||
|
||||
const yarray = ydocUpdated.getArray('array')
|
||||
yarray.observeDeep((events, tr) => {
|
||||
calledEvent++
|
||||
const event = events.find(event => event.target === yarray) || new Y.YEvent(yarray, tr, new Set(null))
|
||||
const d = event.deltaDeep
|
||||
const expectedD = delta.create().insert([delta.create('test').insert('test content').set('k', 'v')])
|
||||
const expectedD = delta.create().insert([delta.create('test').insert('test content').setAttr('k', 'v')])
|
||||
t.compare(d, expectedD)
|
||||
})
|
||||
ydocUpdated.getArray('array').insert(0, [yf])
|
||||
t.assert(calledEvent === 1)
|
||||
const d = Y.diffDocsToDelta(ydocStart, ydocUpdated)
|
||||
console.log('calculated diff', d.toJSON())
|
||||
t.compare(d, delta.create()
|
||||
.update('array', delta.create().insert([delta.create('test').insert('test content', null, { insert: [] }).set('k', 'v', { insert: [] })], null, { insert: [] }))
|
||||
)
|
||||
const expected = delta.create()
|
||||
.modifyAttr('array', delta.create().insert([delta.create('test').insert('test content', null, { insert: [] }).setAttr('k', 'v', { insert: [] })], null, { insert: [] }))
|
||||
t.compare(d.done(), expected.done())
|
||||
}
|
||||
|
||||
@@ -84,7 +84,7 @@ export const testDeltaBasicSchema = _tc => {
|
||||
* @param {t.TestCase} _tc
|
||||
*/
|
||||
export const testDeltaValues = _tc => {
|
||||
const change = delta.create().set('a', 42).unset('b').retain(5).delete(6).insert('!').insert([{ my: 'custom object' }])
|
||||
const change = delta.create().setAttr('a', 42).deleteAttr('b').retain(5).delete(6).insert('!').insert([{ my: 'custom object' }])
|
||||
// iterate through attribute changes
|
||||
for (const attrChange of change.attrs) {
|
||||
if (delta.$insertOp.check(attrChange)) {
|
||||
@@ -122,7 +122,7 @@ export const testBasics = _tc => {
|
||||
const ydoc = new Y.Doc()
|
||||
const ytype = ydoc.get('my data')
|
||||
/**
|
||||
* @type {delta.Delta<any,{ a: number }, { my: string }, string>}
|
||||
* @type {delta.Delta<{attrs: { a: number }, children: { my: string }, text: true }>}
|
||||
*/
|
||||
let observedDelta = delta.create()
|
||||
ytype.observe(event => {
|
||||
@@ -130,7 +130,7 @@ export const testBasics = _tc => {
|
||||
console.log('ytype changed:', observedDelta.toJSON())
|
||||
})
|
||||
// define a change: set attribute: a=42
|
||||
const attrChange = delta.create().set('a', 42).done()
|
||||
const attrChange = delta.create().setAttr('a', 42).done()
|
||||
// define a change: insert textual content and an object
|
||||
const childChange = delta.create().insert('hello').insert([{ my: 'object' }]).done()
|
||||
// merge changes
|
||||
|
||||
@@ -299,8 +299,8 @@ export const testUndoXml = tc => {
|
||||
// format textchild and revert that change
|
||||
undoManager.stopCapturing()
|
||||
textchild.format(3, 4, { bold: true })
|
||||
const v1 = delta.create('UNDEFINED').insert([delta.create('p').insert([delta.text().insert('con').insert('tent', { bold: true }).done()]).done()]).done()
|
||||
const v2 = delta.create('UNDEFINED').insert([delta.create('p').insert([delta.text().insert('content').done()]).done()]).done()
|
||||
const v1 = delta.create('UNDEFINED').insert([delta.create('p').insert([delta.create().insert('con').insert('tent', { bold: true }).done()]).done()]).done()
|
||||
const v2 = delta.create('UNDEFINED').insert([delta.create('p').insert([delta.create().insert('content').done()]).done()]).done()
|
||||
t.compare(xml0.getContentDeep(), v1)
|
||||
undoManager.undo()
|
||||
t.compare(xml0.getContentDeep(), v2)
|
||||
|
||||
Reference in New Issue
Block a user