@blocksuite/virgo
Advanced tools
Comparing version 0.5.0-20230303194346-1eb65e7 to 0.5.0-20230304183717-8ea9932
@@ -21,4 +21,3 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { | ||
render() { | ||
return html ` | ||
<style> | ||
return html `<style> | ||
virgo-line { | ||
@@ -28,4 +27,3 @@ display: block; | ||
</style> | ||
<div>${this.elements}</div> | ||
`; | ||
<div>${this.elements}</div>`; | ||
} | ||
@@ -32,0 +30,0 @@ createRenderRoot() { |
@@ -74,3 +74,16 @@ import { expect, test } from 'vitest'; | ||
length: 0, | ||
})).toEqual([]); | ||
})).toEqual([ | ||
[ | ||
{ | ||
insert: 'aaa', | ||
attributes: { | ||
bold: true, | ||
}, | ||
}, | ||
{ | ||
index: 0, | ||
length: 3, | ||
}, | ||
], | ||
]); | ||
expect(virgo.getDeltasByVRange({ | ||
@@ -141,6 +154,2 @@ index: 0, | ||
index: 3, | ||
length: 0, | ||
})).toEqual([]); | ||
expect(virgo.getDeltasByVRange({ | ||
index: 3, | ||
length: 1, | ||
@@ -150,2 +159,14 @@ })).toEqual([ | ||
{ | ||
insert: 'aaa', | ||
attributes: { | ||
bold: true, | ||
}, | ||
}, | ||
{ | ||
index: 0, | ||
length: 3, | ||
}, | ||
], | ||
[ | ||
{ | ||
insert: 'bbb', | ||
@@ -168,2 +189,14 @@ attributes: { | ||
{ | ||
insert: 'aaa', | ||
attributes: { | ||
bold: true, | ||
}, | ||
}, | ||
{ | ||
index: 0, | ||
length: 3, | ||
}, | ||
], | ||
[ | ||
{ | ||
insert: 'bbb', | ||
@@ -186,2 +219,14 @@ attributes: { | ||
{ | ||
insert: 'aaa', | ||
attributes: { | ||
bold: true, | ||
}, | ||
}, | ||
{ | ||
index: 0, | ||
length: 3, | ||
}, | ||
], | ||
[ | ||
{ | ||
insert: 'bbb', | ||
@@ -188,0 +233,0 @@ attributes: { |
@@ -23,3 +23,4 @@ import { Slot } from '@blocksuite/global/utils'; | ||
private _rootElement; | ||
private _rootElementAbort; | ||
private _mountAbort; | ||
private _handlerAbort; | ||
private _vRange; | ||
@@ -31,3 +32,3 @@ private _isComposing; | ||
private _attributesSchema; | ||
private _onKeyDown; | ||
private _handlers; | ||
private _parseSchema; | ||
@@ -43,3 +44,3 @@ private _renderDeltas; | ||
setAttributesRenderer: (renderer: AttributesRenderer<TextAttributes>) => void; | ||
bindKeyDownHandler(handler: (event: KeyboardEvent) => void): void; | ||
bindHandlers(handlers?: VEditor['_handlers']): void; | ||
mount(rootElement: HTMLElement): void; | ||
@@ -51,11 +52,6 @@ unmount(): void; | ||
getLine(rangeIndex: VRange['index']): readonly [VirgoLine, number]; | ||
/** | ||
* In following example, the vRange is { index: 3, length: 3 } and | ||
* conatins three deltas | ||
* aaa|bbb|ccc | ||
* getDeltasByVRange(...) will just return bbb delta | ||
*/ | ||
getDeltasByVRange(vRange: VRange): DeltaEntry[]; | ||
getVRange(): VRange | null; | ||
getReadOnly(): boolean; | ||
getFormat(vRange: VRange): TextAttributes; | ||
setReadOnly(isReadOnly: boolean): void; | ||
@@ -67,3 +63,3 @@ setVRange(vRange: VRange): void; | ||
insertLineBreak(vRange: VRange): void; | ||
formatText(vRange: VRange, attributes: TextAttributes, options?: { | ||
formatText(vRange: VRange, attributes: Partial<Record<keyof TextAttributes, TextAttributes[keyof TextAttributes] | null>>, options?: { | ||
match?: (delta: DeltaInsert, deltaVRange: VRange) => boolean; | ||
@@ -113,3 +109,2 @@ mode?: 'replace' | 'merge'; | ||
private _onSelectionChange; | ||
private _onPaste; | ||
private _onUpdateVRange; | ||
@@ -116,0 +111,0 @@ private _transact; |
@@ -78,3 +78,4 @@ import { assertExists, Slot } from '@blocksuite/global/utils'; | ||
this._rootElement = null; | ||
this._rootElementAbort = null; | ||
this._mountAbort = null; | ||
this._handlerAbort = null; | ||
this._vRange = null; | ||
@@ -85,5 +86,3 @@ this._isComposing = false; | ||
this._attributesSchema = baseTextAttributes; | ||
this._onKeyDown = () => { | ||
return; | ||
}; | ||
this._handlers = {}; | ||
this._parseSchema = (textAttributes) => { | ||
@@ -143,18 +142,2 @@ return this._attributesSchema.optional().parse(textAttributes); | ||
}; | ||
this._onPaste = (event) => { | ||
const data = event.clipboardData?.getData('text/plain'); | ||
if (data) { | ||
const vRange = this._vRange; | ||
if (vRange) { | ||
this.insertText(vRange, data); | ||
this.slots.updateVRange.emit([ | ||
{ | ||
index: vRange.index + data.length, | ||
length: 0, | ||
}, | ||
'input', | ||
]); | ||
} | ||
} | ||
}; | ||
this._onUpdateVRange = ([newVRange, origin]) => { | ||
@@ -195,4 +178,36 @@ this._vRange = newVRange; | ||
} | ||
bindKeyDownHandler(handler) { | ||
this._onKeyDown = handler; | ||
bindHandlers(handlers = { | ||
paste: (event) => { | ||
const data = event.clipboardData?.getData('text/plain'); | ||
if (data) { | ||
const vRange = this._vRange; | ||
if (vRange) { | ||
this.insertText(vRange, data); | ||
this.slots.updateVRange.emit([ | ||
{ | ||
index: vRange.index + data.length, | ||
length: 0, | ||
}, | ||
'input', | ||
]); | ||
} | ||
} | ||
}, | ||
}) { | ||
this._handlers = handlers; | ||
if (this._handlerAbort) { | ||
this._handlerAbort.abort(); | ||
} | ||
this._handlerAbort = new AbortController(); | ||
assertExists(this._rootElement, 'you need to mount the editor first'); | ||
if (this._handlers.paste) { | ||
this._rootElement.addEventListener('paste', this._handlers.paste, { | ||
signal: this._handlerAbort.signal, | ||
}); | ||
} | ||
if (this._handlers.keydown) { | ||
this._rootElement.addEventListener('keydown', this._handlers.keydown, { | ||
signal: this._handlerAbort.signal, | ||
}); | ||
} | ||
} | ||
@@ -206,6 +221,6 @@ mount(rootElement) { | ||
document.addEventListener('selectionchange', this._onSelectionChange); | ||
this._rootElementAbort = new AbortController(); | ||
this._mountAbort = new AbortController(); | ||
this._renderDeltas(); | ||
this._rootElement.addEventListener('beforeinput', this._onBeforeInput.bind(this), { | ||
signal: this._rootElementAbort.signal, | ||
signal: this._mountAbort.signal, | ||
}); | ||
@@ -220,20 +235,18 @@ this._rootElement | ||
this._rootElement.addEventListener('compositionstart', this._onCompositionStart.bind(this), { | ||
signal: this._rootElementAbort.signal, | ||
signal: this._mountAbort.signal, | ||
}); | ||
this._rootElement.addEventListener('compositionend', this._onCompositionEnd.bind(this), { | ||
signal: this._rootElementAbort.signal, | ||
signal: this._mountAbort.signal, | ||
}); | ||
this._rootElement.addEventListener('keydown', this._onKeyDown, { | ||
signal: this._rootElementAbort.signal, | ||
}); | ||
this._rootElement.addEventListener('paste', this._onPaste, { | ||
signal: this._rootElementAbort.signal, | ||
}); | ||
} | ||
unmount() { | ||
document.removeEventListener('selectionchange', this._onSelectionChange); | ||
if (this._rootElementAbort) { | ||
this._rootElementAbort.abort(); | ||
this._rootElementAbort = null; | ||
if (this._mountAbort) { | ||
this._mountAbort.abort(); | ||
this._mountAbort = null; | ||
} | ||
if (this._handlerAbort) { | ||
this._handlerAbort.abort(); | ||
this._handlerAbort = null; | ||
} | ||
this._rootElement?.replaceChildren(); | ||
@@ -271,5 +284,2 @@ this._rootElement = null; | ||
} | ||
if (textElement.textContent === ZERO_WIDTH_SPACE) { | ||
continue; | ||
} | ||
if (index + textElement.textContent.length >= rangeIndex) { | ||
@@ -303,11 +313,3 @@ const text = getTextNodeFromElement(textElement); | ||
} | ||
/** | ||
* In following example, the vRange is { index: 3, length: 3 } and | ||
* conatins three deltas | ||
* aaa|bbb|ccc | ||
* getDeltasByVRange(...) will just return bbb delta | ||
*/ | ||
getDeltasByVRange(vRange) { | ||
if (vRange.length <= 0) | ||
return []; | ||
const deltas = this.yText.toDelta(); | ||
@@ -318,4 +320,5 @@ const result = []; | ||
const delta = deltas[i]; | ||
if (index + delta.insert.length > vRange.index && | ||
index < vRange.index + vRange.length) { | ||
if (index + delta.insert.length >= vRange.index && | ||
(index < vRange.index + vRange.length || | ||
(vRange.length === 0 && index === vRange.index))) { | ||
result.push([delta, { index, length: delta.insert.length }]); | ||
@@ -333,2 +336,19 @@ } | ||
} | ||
getFormat(vRange) { | ||
const deltas = this.getDeltasByVRange(vRange); | ||
const result = {}; | ||
for (const [delta] of deltas) { | ||
if (delta.attributes) { | ||
for (const [key, value] of Object.entries(delta.attributes)) { | ||
if (typeof value === 'boolean' && !value) { | ||
delete result[key]; | ||
} | ||
else { | ||
result[key] = value; | ||
} | ||
} | ||
} | ||
} | ||
return result; | ||
} | ||
setReadOnly(isReadOnly) { | ||
@@ -355,3 +375,3 @@ this._isReadOnly = isReadOnly; | ||
this.yText.delete(vRange.index, vRange.length); | ||
this.yText.insert(vRange.index, text); | ||
this.yText.insert(vRange.index, text, {}); | ||
}); | ||
@@ -358,0 +378,0 @@ } |
{ | ||
"name": "@blocksuite/virgo", | ||
"version": "0.5.0-20230303194346-1eb65e7", | ||
"version": "0.5.0-20230304183717-8ea9932", | ||
"description": "A micro editor.", | ||
@@ -26,3 +26,3 @@ "main": "dist/index.js", | ||
"dependencies": { | ||
"@blocksuite/global": "0.5.0-20230303194346-1eb65e7", | ||
"@blocksuite/global": "0.5.0-20230304183717-8ea9932", | ||
"zod": "^3.20.6" | ||
@@ -29,0 +29,0 @@ }, |
@@ -23,4 +23,3 @@ import { html, LitElement } from 'lit'; | ||
render() { | ||
return html` | ||
<style> | ||
return html`<style> | ||
virgo-line { | ||
@@ -30,4 +29,3 @@ display: block; | ||
</style> | ||
<div>${this.elements}</div> | ||
`; | ||
<div>${this.elements}</div>`; | ||
} | ||
@@ -34,0 +32,0 @@ |
@@ -84,3 +84,16 @@ import { expect, test } from 'vitest'; | ||
}) | ||
).toEqual([]); | ||
).toEqual([ | ||
[ | ||
{ | ||
insert: 'aaa', | ||
attributes: { | ||
bold: true, | ||
}, | ||
}, | ||
{ | ||
index: 0, | ||
length: 3, | ||
}, | ||
], | ||
]); | ||
@@ -162,9 +175,2 @@ expect( | ||
index: 3, | ||
length: 0, | ||
}) | ||
).toEqual([]); | ||
expect( | ||
virgo.getDeltasByVRange({ | ||
index: 3, | ||
length: 1, | ||
@@ -175,2 +181,14 @@ }) | ||
{ | ||
insert: 'aaa', | ||
attributes: { | ||
bold: true, | ||
}, | ||
}, | ||
{ | ||
index: 0, | ||
length: 3, | ||
}, | ||
], | ||
[ | ||
{ | ||
insert: 'bbb', | ||
@@ -196,2 +214,14 @@ attributes: { | ||
{ | ||
insert: 'aaa', | ||
attributes: { | ||
bold: true, | ||
}, | ||
}, | ||
{ | ||
index: 0, | ||
length: 3, | ||
}, | ||
], | ||
[ | ||
{ | ||
insert: 'bbb', | ||
@@ -217,2 +247,14 @@ attributes: { | ||
{ | ||
insert: 'aaa', | ||
attributes: { | ||
bold: true, | ||
}, | ||
}, | ||
{ | ||
index: 0, | ||
length: 3, | ||
}, | ||
], | ||
[ | ||
{ | ||
insert: 'bbb', | ||
@@ -219,0 +261,0 @@ attributes: { |
138
src/virgo.ts
@@ -122,3 +122,4 @@ import { assertExists, Slot } from '@blocksuite/global/utils'; | ||
private _rootElement: HTMLElement | null = null; | ||
private _rootElementAbort: AbortController | null = null; | ||
private _mountAbort: AbortController | null = null; | ||
private _handlerAbort: AbortController | null = null; | ||
private _vRange: VRange | null = null; | ||
@@ -135,5 +136,6 @@ private _isComposing = false; | ||
private _onKeyDown: (event: KeyboardEvent) => void = () => { | ||
return; | ||
}; | ||
private _handlers: { | ||
keydown?: (event: KeyboardEvent) => void; | ||
paste?: (event: ClipboardEvent) => void; | ||
} = {}; | ||
@@ -209,4 +211,42 @@ private _parseSchema = (textAttributes?: TextAttributes) => { | ||
bindKeyDownHandler(handler: (event: KeyboardEvent) => void) { | ||
this._onKeyDown = handler; | ||
bindHandlers( | ||
handlers: VEditor['_handlers'] = { | ||
paste: (event: ClipboardEvent) => { | ||
const data = event.clipboardData?.getData('text/plain'); | ||
if (data) { | ||
const vRange = this._vRange; | ||
if (vRange) { | ||
this.insertText(vRange, data); | ||
this.slots.updateVRange.emit([ | ||
{ | ||
index: vRange.index + data.length, | ||
length: 0, | ||
}, | ||
'input', | ||
]); | ||
} | ||
} | ||
}, | ||
} | ||
) { | ||
this._handlers = handlers; | ||
if (this._handlerAbort) { | ||
this._handlerAbort.abort(); | ||
} | ||
this._handlerAbort = new AbortController(); | ||
assertExists(this._rootElement, 'you need to mount the editor first'); | ||
if (this._handlers.paste) { | ||
this._rootElement.addEventListener('paste', this._handlers.paste, { | ||
signal: this._handlerAbort.signal, | ||
}); | ||
} | ||
if (this._handlers.keydown) { | ||
this._rootElement.addEventListener('keydown', this._handlers.keydown, { | ||
signal: this._handlerAbort.signal, | ||
}); | ||
} | ||
} | ||
@@ -222,3 +262,3 @@ | ||
this._rootElementAbort = new AbortController(); | ||
this._mountAbort = new AbortController(); | ||
@@ -231,3 +271,3 @@ this._renderDeltas(); | ||
{ | ||
signal: this._rootElementAbort.signal, | ||
signal: this._mountAbort.signal, | ||
} | ||
@@ -247,3 +287,3 @@ ); | ||
{ | ||
signal: this._rootElementAbort.signal, | ||
signal: this._mountAbort.signal, | ||
} | ||
@@ -255,12 +295,5 @@ ); | ||
{ | ||
signal: this._rootElementAbort.signal, | ||
signal: this._mountAbort.signal, | ||
} | ||
); | ||
this._rootElement.addEventListener('keydown', this._onKeyDown, { | ||
signal: this._rootElementAbort.signal, | ||
}); | ||
this._rootElement.addEventListener('paste', this._onPaste, { | ||
signal: this._rootElementAbort.signal, | ||
}); | ||
} | ||
@@ -270,7 +303,12 @@ | ||
document.removeEventListener('selectionchange', this._onSelectionChange); | ||
if (this._rootElementAbort) { | ||
this._rootElementAbort.abort(); | ||
this._rootElementAbort = null; | ||
if (this._mountAbort) { | ||
this._mountAbort.abort(); | ||
this._mountAbort = null; | ||
} | ||
if (this._handlerAbort) { | ||
this._handlerAbort.abort(); | ||
this._handlerAbort = null; | ||
} | ||
this._rootElement?.replaceChildren(); | ||
@@ -316,5 +354,2 @@ | ||
} | ||
if (textElement.textContent === ZERO_WIDTH_SPACE) { | ||
continue; | ||
} | ||
if (index + textElement.textContent.length >= rangeIndex) { | ||
@@ -357,11 +392,3 @@ const text = getTextNodeFromElement(textElement); | ||
/** | ||
* In following example, the vRange is { index: 3, length: 3 } and | ||
* conatins three deltas | ||
* aaa|bbb|ccc | ||
* getDeltasByVRange(...) will just return bbb delta | ||
*/ | ||
getDeltasByVRange(vRange: VRange): DeltaEntry[] { | ||
if (vRange.length <= 0) return []; | ||
const deltas = this.yText.toDelta() as DeltaInsert[]; | ||
@@ -374,4 +401,5 @@ | ||
if ( | ||
index + delta.insert.length > vRange.index && | ||
index < vRange.index + vRange.length | ||
index + delta.insert.length >= vRange.index && | ||
(index < vRange.index + vRange.length || | ||
(vRange.length === 0 && index === vRange.index)) | ||
) { | ||
@@ -394,2 +422,23 @@ result.push([delta, { index, length: delta.insert.length }]); | ||
getFormat(vRange: VRange): TextAttributes { | ||
const deltas = this.getDeltasByVRange(vRange); | ||
const result: { | ||
[key: string]: unknown; | ||
} = {}; | ||
for (const [delta] of deltas) { | ||
if (delta.attributes) { | ||
for (const [key, value] of Object.entries(delta.attributes)) { | ||
if (typeof value === 'boolean' && !value) { | ||
delete result[key]; | ||
} else { | ||
result[key] = value; | ||
} | ||
} | ||
} | ||
} | ||
return result as TextAttributes; | ||
} | ||
setReadOnly(isReadOnly: boolean): void { | ||
@@ -421,3 +470,3 @@ this._isReadOnly = isReadOnly; | ||
this.yText.delete(vRange.index, vRange.length); | ||
this.yText.insert(vRange.index, text); | ||
this.yText.insert(vRange.index, text, {}); | ||
}); | ||
@@ -435,3 +484,5 @@ } | ||
vRange: VRange, | ||
attributes: TextAttributes, | ||
attributes: Partial< | ||
Record<keyof TextAttributes, TextAttributes[keyof TextAttributes] | null> | ||
>, | ||
options: { | ||
@@ -856,19 +907,2 @@ match?: (delta: DeltaInsert, deltaVRange: VRange) => boolean; | ||
private _onPaste = (event: ClipboardEvent) => { | ||
const data = event.clipboardData?.getData('text/plain'); | ||
if (data) { | ||
const vRange = this._vRange; | ||
if (vRange) { | ||
this.insertText(vRange, data); | ||
this.slots.updateVRange.emit([ | ||
{ | ||
index: vRange.index + data.length, | ||
length: 0, | ||
}, | ||
'input', | ||
]); | ||
} | ||
} | ||
}; | ||
private _onUpdateVRange = ([newVRange, origin]: UpdateVRangeProp) => { | ||
@@ -875,0 +909,0 @@ this._vRange = newVRange; |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
341284
4489
+ Added@blocksuite/global@0.5.0-20230304183717-8ea9932(transitive)
- Removed@blocksuite/global@0.5.0-20230303194346-1eb65e7(transitive)