@blocksuite/virgo
Advanced tools
Comparing version 0.4.0-alpha.4 to 0.4.0
import { LitElement } from 'lit'; | ||
import { z } from 'zod'; | ||
import type { DeltaInsert } from '../types.js'; | ||
export declare const baseTextAttributes: z.ZodOptional<z.ZodObject<{ | ||
export declare const baseTextAttributes: z.ZodObject<{ | ||
bold: z.ZodOptional<z.ZodBoolean>; | ||
italic: z.ZodOptional<z.ZodBoolean>; | ||
underline: z.ZodOptional<z.ZodBoolean>; | ||
strikethrough: z.ZodOptional<z.ZodBoolean>; | ||
inlineCode: z.ZodOptional<z.ZodBoolean>; | ||
color: z.ZodOptional<z.ZodString>; | ||
strike: z.ZodOptional<z.ZodBoolean>; | ||
code: z.ZodOptional<z.ZodBoolean>; | ||
link: z.ZodOptional<z.ZodString>; | ||
@@ -16,5 +15,4 @@ }, "strip", z.ZodTypeAny, { | ||
underline?: boolean | undefined; | ||
strikethrough?: boolean | undefined; | ||
inlineCode?: boolean | undefined; | ||
color?: string | undefined; | ||
strike?: boolean | undefined; | ||
code?: boolean | undefined; | ||
link?: string | undefined; | ||
@@ -25,7 +23,6 @@ }, { | ||
underline?: boolean | undefined; | ||
strikethrough?: boolean | undefined; | ||
inlineCode?: boolean | undefined; | ||
color?: string | undefined; | ||
strike?: boolean | undefined; | ||
code?: boolean | undefined; | ||
link?: string | undefined; | ||
}>>; | ||
}>; | ||
export type BaseTextAttributes = z.infer<typeof baseTextAttributes>; | ||
@@ -32,0 +29,0 @@ export declare class BaseText extends LitElement { |
@@ -13,16 +13,11 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { | ||
import { VirgoUnitText } from './virgo-unit-text.js'; | ||
export const baseTextAttributes = z | ||
.object({ | ||
export const baseTextAttributes = z.object({ | ||
bold: z.boolean().optional(), | ||
italic: z.boolean().optional(), | ||
underline: z.boolean().optional(), | ||
strikethrough: z.boolean().optional(), | ||
inlineCode: z.boolean().optional(), | ||
color: z.string().optional(), | ||
strike: z.boolean().optional(), | ||
code: z.boolean().optional(), | ||
link: z.string().optional(), | ||
}) | ||
.optional(); | ||
}); | ||
function virgoTextStyles(props) { | ||
if (!props) | ||
return styleMap({}); | ||
let textDecorations = ''; | ||
@@ -32,7 +27,7 @@ if (props.underline) { | ||
} | ||
if (props.strikethrough) { | ||
if (props.strike) { | ||
textDecorations += ' line-through'; | ||
} | ||
let inlineCodeStyle = {}; | ||
if (props.inlineCode) { | ||
if (props.code) { | ||
inlineCodeStyle = { | ||
@@ -66,7 +61,8 @@ 'font-family': '"SFMono-Regular", Menlo, Consolas, "PT Mono", "Liberation Mono", Courier, monospace', | ||
unitText.str = this.delta.insert; | ||
const style = this.delta.attributes | ||
? virgoTextStyles(this.delta.attributes) | ||
: styleMap({}); | ||
// we need to avoid \n appearing before and after the span element, which will | ||
// cause the unexpected space | ||
return html `<span | ||
data-virgo-element="true" | ||
style=${virgoTextStyles(this.delta.attributes)} | ||
return html `<span data-virgo-element="true" style=${style} | ||
>${unitText}</span | ||
@@ -73,0 +69,0 @@ >`; |
@@ -515,2 +515,27 @@ import { expect, test } from '@playwright/test'; | ||
}); | ||
test('select from the start of line using shift+arrow', async ({ page }) => { | ||
await enterVirgoPlayground(page); | ||
await focusVirgoRichText(page); | ||
const editorA = page.locator('[data-virgo-root="true"]').nth(0); | ||
const editorB = page.locator('[data-virgo-root="true"]').nth(1); | ||
expect(await editorA.innerText()).toBe(ZERO_WIDTH_SPACE); | ||
expect(await editorB.innerText()).toBe(ZERO_WIDTH_SPACE); | ||
await type(page, 'abc'); | ||
await page.keyboard.press('Enter', { delay: 50 }); | ||
await type(page, 'def'); | ||
await page.keyboard.press('Enter', { delay: 50 }); | ||
await type(page, 'ghi'); | ||
expect(await editorA.innerText()).toBe('abc\ndef\nghi'); | ||
expect(await editorB.innerText()).toBe('abc\ndef\nghi'); | ||
await page.keyboard.press('ArrowLeft'); | ||
await page.keyboard.press('ArrowLeft'); | ||
await page.keyboard.press('ArrowLeft'); | ||
await page.keyboard.down('Shift'); | ||
await page.keyboard.press('ArrowUp'); | ||
await page.keyboard.press('ArrowUp'); | ||
await page.keyboard.press('ArrowRight'); | ||
await page.keyboard.press('Backspace'); | ||
expect(await editorA.innerText()).toBe('aghi'); | ||
expect(await editorB.innerText()).toBe('aghi'); | ||
}); | ||
//# sourceMappingURL=virgo.spec.js.map |
@@ -6,3 +6,3 @@ import { BaseText, baseTextAttributes } from '../components/base-text.js'; | ||
export function baseRenderElement(delta) { | ||
const parseResult = baseTextAttributes.parse(delta.attributes); | ||
const parseResult = baseTextAttributes.optional().parse(delta.attributes); | ||
const baseText = new BaseText(); | ||
@@ -9,0 +9,0 @@ baseText.delta = { |
@@ -40,3 +40,3 @@ import { Signal } from '@blocksuite/global/utils'; | ||
insertLineBreak(vRange: VRange): void; | ||
formatText(vRange: VRange, attributes: NonNullable<TextAttributes>, options?: { | ||
formatText(vRange: VRange, attributes: TextAttributes, options?: { | ||
match?: (delta: DeltaInsert, deltaVRange: VRange) => boolean; | ||
@@ -86,2 +86,3 @@ mode?: 'replace' | 'merge'; | ||
private _onSelectionChange; | ||
private _onPaste; | ||
private _onUpdateVRange; | ||
@@ -88,0 +89,0 @@ private _transact; |
@@ -46,2 +46,18 @@ import { assertExists, Signal } from '@blocksuite/global/utils'; | ||
}; | ||
this._onPaste = (event) => { | ||
const data = event.clipboardData?.getData('text/plain'); | ||
if (data) { | ||
const vRange = this._vRange; | ||
if (vRange) { | ||
this.insertText(vRange, data); | ||
this.signals.updateVRange.emit([ | ||
{ | ||
index: vRange.index + data.length, | ||
length: 0, | ||
}, | ||
'input', | ||
]); | ||
} | ||
} | ||
}; | ||
this._onUpdateVRange = ([newRangStatic, origin]) => { | ||
@@ -107,2 +123,5 @@ this._vRange = newRangStatic; | ||
}); | ||
this._rootElement.addEventListener('paste', this._onPaste, { | ||
signal: this._rootElementAbort.signal, | ||
}); | ||
} | ||
@@ -338,2 +357,13 @@ unmount() { | ||
} | ||
else if (anchorNode instanceof HTMLElement && | ||
anchorNode.parentElement instanceof VirgoLine) { | ||
const firstTextElement = anchorNode.querySelector('v-text'); | ||
if (firstTextElement) { | ||
const textNode = getTextNodeFromElement(firstTextElement); | ||
if (textNode) { | ||
anchorText = textNode; | ||
anchorTextOffset = 0; | ||
} | ||
} | ||
} | ||
if (focusNode instanceof Text && isVText(focusNode)) { | ||
@@ -351,2 +381,13 @@ focusText = focusNode; | ||
} | ||
else if (focusNode instanceof HTMLElement && | ||
focusNode.parentElement instanceof VirgoLine) { | ||
const firstTextElement = focusNode.querySelector('v-text'); | ||
if (firstTextElement) { | ||
const textNode = getTextNodeFromElement(firstTextElement); | ||
if (textNode) { | ||
anchorText = textNode; | ||
anchorTextOffset = 0; | ||
} | ||
} | ||
} | ||
// case 1 | ||
@@ -353,0 +394,0 @@ if (anchorText && focusText) { |
{ | ||
"name": "@blocksuite/virgo", | ||
"version": "0.4.0-alpha.4", | ||
"version": "0.4.0", | ||
"description": "A micro editor.", | ||
@@ -26,3 +26,3 @@ "main": "dist/index.js", | ||
"dependencies": { | ||
"@blocksuite/global": "0.4.0-alpha.4", | ||
"@blocksuite/global": "0.4.0", | ||
"zod": "^3.20.6" | ||
@@ -29,0 +29,0 @@ }, |
@@ -10,13 +10,10 @@ import { html, LitElement } from 'lit'; | ||
export const baseTextAttributes = z | ||
.object({ | ||
bold: z.boolean().optional(), | ||
italic: z.boolean().optional(), | ||
underline: z.boolean().optional(), | ||
strikethrough: z.boolean().optional(), | ||
inlineCode: z.boolean().optional(), | ||
color: z.string().optional(), | ||
link: z.string().optional(), | ||
}) | ||
.optional(); | ||
export const baseTextAttributes = z.object({ | ||
bold: z.boolean().optional(), | ||
italic: z.boolean().optional(), | ||
underline: z.boolean().optional(), | ||
strike: z.boolean().optional(), | ||
code: z.boolean().optional(), | ||
link: z.string().optional(), | ||
}); | ||
@@ -28,4 +25,2 @@ export type BaseTextAttributes = z.infer<typeof baseTextAttributes>; | ||
): ReturnType<typeof styleMap> { | ||
if (!props) return styleMap({}); | ||
let textDecorations = ''; | ||
@@ -35,3 +30,3 @@ if (props.underline) { | ||
} | ||
if (props.strikethrough) { | ||
if (props.strike) { | ||
textDecorations += ' line-through'; | ||
@@ -41,3 +36,3 @@ } | ||
let inlineCodeStyle = {}; | ||
if (props.inlineCode) { | ||
if (props.code) { | ||
inlineCodeStyle = { | ||
@@ -74,8 +69,9 @@ 'font-family': | ||
unitText.str = this.delta.insert; | ||
const style = this.delta.attributes | ||
? virgoTextStyles(this.delta.attributes) | ||
: styleMap({}); | ||
// we need to avoid \n appearing before and after the span element, which will | ||
// cause the unexpected space | ||
return html`<span | ||
data-virgo-element="true" | ||
style=${virgoTextStyles(this.delta.attributes)} | ||
return html`<span data-virgo-element="true" style=${style} | ||
>${unitText}</span | ||
@@ -82,0 +78,0 @@ >`; |
@@ -618,1 +618,34 @@ import { expect, test } from '@playwright/test'; | ||
}); | ||
test('select from the start of line using shift+arrow', async ({ page }) => { | ||
await enterVirgoPlayground(page); | ||
await focusVirgoRichText(page); | ||
const editorA = page.locator('[data-virgo-root="true"]').nth(0); | ||
const editorB = page.locator('[data-virgo-root="true"]').nth(1); | ||
expect(await editorA.innerText()).toBe(ZERO_WIDTH_SPACE); | ||
expect(await editorB.innerText()).toBe(ZERO_WIDTH_SPACE); | ||
await type(page, 'abc'); | ||
await page.keyboard.press('Enter', { delay: 50 }); | ||
await type(page, 'def'); | ||
await page.keyboard.press('Enter', { delay: 50 }); | ||
await type(page, 'ghi'); | ||
expect(await editorA.innerText()).toBe('abc\ndef\nghi'); | ||
expect(await editorB.innerText()).toBe('abc\ndef\nghi'); | ||
await page.keyboard.press('ArrowLeft'); | ||
await page.keyboard.press('ArrowLeft'); | ||
await page.keyboard.press('ArrowLeft'); | ||
await page.keyboard.down('Shift'); | ||
await page.keyboard.press('ArrowUp'); | ||
await page.keyboard.press('ArrowUp'); | ||
await page.keyboard.press('ArrowRight'); | ||
await page.keyboard.press('Backspace'); | ||
expect(await editorA.innerText()).toBe('aghi'); | ||
expect(await editorB.innerText()).toBe('aghi'); | ||
}); |
@@ -8,3 +8,3 @@ import { BaseText, baseTextAttributes } from '../components/base-text.js'; | ||
export function baseRenderElement(delta: DeltaInsert): TextElement { | ||
const parseResult = baseTextAttributes.parse(delta.attributes); | ||
const parseResult = baseTextAttributes.optional().parse(delta.attributes); | ||
@@ -11,0 +11,0 @@ const baseText = new BaseText(); |
@@ -117,2 +117,5 @@ import { assertExists, Signal } from '@blocksuite/global/utils'; | ||
}); | ||
this._rootElement.addEventListener('paste', this._onPaste, { | ||
signal: this._rootElementAbort.signal, | ||
}); | ||
} | ||
@@ -232,3 +235,3 @@ | ||
vRange: VRange, | ||
attributes: NonNullable<TextAttributes>, | ||
attributes: TextAttributes, | ||
options: { | ||
@@ -419,2 +422,14 @@ match?: (delta: DeltaInsert, deltaVRange: VRange) => boolean; | ||
} | ||
} else if ( | ||
anchorNode instanceof HTMLElement && | ||
anchorNode.parentElement instanceof VirgoLine | ||
) { | ||
const firstTextElement = anchorNode.querySelector('v-text'); | ||
if (firstTextElement) { | ||
const textNode = getTextNodeFromElement(firstTextElement); | ||
if (textNode) { | ||
anchorText = textNode; | ||
anchorTextOffset = 0; | ||
} | ||
} | ||
} | ||
@@ -433,4 +448,15 @@ if (focusNode instanceof Text && isVText(focusNode)) { | ||
} | ||
} else if ( | ||
focusNode instanceof HTMLElement && | ||
focusNode.parentElement instanceof VirgoLine | ||
) { | ||
const firstTextElement = focusNode.querySelector('v-text'); | ||
if (firstTextElement) { | ||
const textNode = getTextNodeFromElement(firstTextElement); | ||
if (textNode) { | ||
anchorText = textNode; | ||
anchorTextOffset = 0; | ||
} | ||
} | ||
} | ||
// case 1 | ||
@@ -659,2 +685,19 @@ if (anchorText && focusText) { | ||
private _onPaste = (event: ClipboardEvent) => { | ||
const data = event.clipboardData?.getData('text/plain'); | ||
if (data) { | ||
const vRange = this._vRange; | ||
if (vRange) { | ||
this.insertText(vRange, data); | ||
this.signals.updateVRange.emit([ | ||
{ | ||
index: vRange.index + data.length, | ||
length: 0, | ||
}, | ||
'input', | ||
]); | ||
} | ||
} | ||
}; | ||
private _onUpdateVRange = ([newRangStatic, origin]: UpdateVRangeProp) => { | ||
@@ -661,0 +704,0 @@ this._vRange = newRangStatic; |
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
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
244854
3507
+ Added@blocksuite/global@0.4.0(transitive)
- Removed@blocksuite/global@0.4.0-alpha.4(transitive)
Updated@blocksuite/global@0.4.0