Skip to content

Commit

Permalink
fix: wrong behavior in option + delete (toeverything#4804)
Browse files Browse the repository at this point in the history
  • Loading branch information
Flrande authored Sep 18, 2023
1 parent a132906 commit 900fcf5
Show file tree
Hide file tree
Showing 5 changed files with 46 additions and 225 deletions.
20 changes: 4 additions & 16 deletions packages/blocks/src/components/rich-text/keymap/container.ts
Original file line number Diff line number Diff line change
Expand Up @@ -240,20 +240,6 @@ export const bindContainerHotkey = (blockElement: BlockElement) => {

return true;
},
'Shift-Enter': () => {
if (!blockElement.selected?.is('text')) return;

const vEditor = _getVirgo();
const vRange = vEditor.getVRange();
assertExists(vRange);
vEditor.insertText(vRange, '\n');
vEditor.setVRange({
index: vRange.index + 1,
length: 0,
});

return true;
},
'Mod-Enter': ctx => {
if (!blockElement.selected?.is('text')) return;

Expand Down Expand Up @@ -341,8 +327,10 @@ export const bindContainerHotkey = (blockElement: BlockElement) => {
const vEditor = _getVirgo();
const vRange = vEditor.getVRange();
assertExists(vRange);
handleRemoveAllIndent(model.page, model, vRange.index);
_preventDefault(ctx);
if (vRange.index === 0) {
handleRemoveAllIndent(model.page, model, vRange.index);
_preventDefault(ctx);
}

return true;
}
Expand Down
2 changes: 0 additions & 2 deletions packages/virgo/src/services/event.ts
Original file line number Diff line number Diff line change
Expand Up @@ -267,8 +267,6 @@ export class VirgoEventService<TextAttributes extends BaseTextAttributes> {

if (!this.editor.getVRange()) return;

// Sometimes input event will directly come from some scripts (e.g. browser extension),
// so we need to resync the vRange.
const targetRanges = event.getTargetRanges();
if (targetRanges.length > 0) {
const staticRange = targetRanges[0];
Expand Down
7 changes: 2 additions & 5 deletions packages/virgo/src/tests/virgo.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -795,11 +795,7 @@ test('getLine', async ({ page }) => {

await page.waitForTimeout(100);

await type(page, 'abc');
await press(page, 'Enter');
await type(page, 'def');
await press(page, 'Enter');
await type(page, 'ghi');
await type(page, 'abc\ndef\nghi');

expect(await editorA.innerText()).toBe('abc\ndef\nghi');
expect(await editorB.innerText()).toBe('abc\ndef\nghi');
Expand Down Expand Up @@ -871,6 +867,7 @@ test('embed', async ({ page }) => {
expect(await editorA.innerText()).toBe('abcde');

await press(page, 'ArrowLeft');
await page.waitForTimeout(100);
page.keyboard.down('Shift');
await press(page, 'ArrowLeft');
await press(page, 'ArrowLeft');
Expand Down
239 changes: 37 additions & 202 deletions packages/virgo/src/utils/transform-input.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,167 +8,37 @@ function handleInsertText<TextAttributes extends BaseTextAttributes>(
editor: VEditor,
attributes: TextAttributes
) {
if (vRange.index >= 0 && data) {
editor.insertText(vRange, data, attributes);
editor.setVRange(
{
index: vRange.index + data.length,
length: 0,
},
false
);
}
if (!data) return;
editor.insertText(vRange, data, attributes);
editor.setVRange(
{
index: vRange.index + data.length,
length: 0,
},
false
);
}

function handleInsertParagraph(vRange: VRange, editor: VEditor) {
if (vRange.index >= 0) {
editor.insertLineBreak(vRange);
editor.setVRange(
{
index: vRange.index + 1,
length: 0,
},
false
);
}
}

function handleDeleteBackward(vRange: VRange, editor: VEditor) {
if (vRange.index >= 0) {
if (vRange.length > 0) {
editor.deleteText(vRange);
editor.setVRange(
{
index: vRange.index,
length: 0,
},
false
);
return;
}

if (vRange.index > 0) {
const originalString = editor.yText.toString().slice(0, vRange.index);
const segments = [...new Intl.Segmenter().segment(originalString)];
const deletedLength = segments[segments.length - 1].segment.length;
editor.deleteText({
index: vRange.index - deletedLength,
length: deletedLength,
});
editor.setVRange(
{
index: vRange.index - deletedLength,
length: 0,
},
false
);
}
}
}

function handleDeleteForward(editor: VEditor, vRange: VRange) {
if (vRange.index < editor.yText.length) {
if (vRange.length > 0) {
editor.deleteText(vRange);
editor.setVRange(
{
index: vRange.index,
length: 0,
},
false
);
} else {
const originalString = editor.yText.toString();
const segments = [...new Intl.Segmenter().segment(originalString)];
const slicedString = originalString.slice(0, vRange.index);
const slicedSegments = [...new Intl.Segmenter().segment(slicedString)];
const deletedLength = segments[slicedSegments.length].segment.length;
editor.deleteText({
index: vRange.index,
length: deletedLength,
});
editor.setVRange(
{
index: vRange.index,
length: 0,
},
false
);
}
}
}

function handleDeleteWordBackward(editor: VEditor, vRange: VRange) {
const matches = /\S+\s*$/.exec(
editor.yText.toString().slice(0, vRange.index)
editor.insertLineBreak(vRange);
editor.setVRange(
{
index: vRange.index + 1,
length: 0,
},
false
);
if (matches) {
const deleteLength = matches[0].length;

editor.deleteText({
index: vRange.index - deleteLength,
length: deleteLength,
});
editor.setVRange(
{
index: vRange.index - deleteLength,
length: 0,
},
false
);
}
}

function handleDeleteWordForward(editor: VEditor, vRange: VRange) {
const matches = /^\s*\S+/.exec(editor.yText.toString().slice(vRange.index));
if (matches) {
const deleteLength = matches[0].length;

editor.deleteText({
function handleDelete(vRange: VRange, editor: VEditor) {
editor.deleteText(vRange);
editor.setVRange(
{
index: vRange.index,
length: deleteLength,
});
editor.setVRange(
{
index: vRange.index,
length: 0,
},
false
);
}
}

function handleDeleteLine(editor: VEditor, vRange: VRange) {
if (vRange.length > 0) {
editor.deleteText(vRange);
editor.setVRange(
{
index: vRange.index,
length: 0,
},
false
);

return;
}

if (vRange.index > 0) {
const str = editor.yText.toString();
const deleteLength =
vRange.index - Math.max(0, str.slice(0, vRange.index).lastIndexOf('\n'));

editor.deleteText({
index: vRange.index - deleteLength,
length: deleteLength,
});
editor.setVRange(
{
index: vRange.index - deleteLength,
length: 0,
},
false
);
}
length: 0,
},
false
);
}

export function transformInput<TextAttributes extends BaseTextAttributes>(
Expand All @@ -178,53 +48,18 @@ export function transformInput<TextAttributes extends BaseTextAttributes>(
vRange: VRange,
editor: VEditor
) {
// You can find explanation of inputType here:
// [Input Events Level 2](https://w3c.github.io/input-events/#interface-InputEvent-Attributes)
switch (inputType) {
case 'insertText': {
handleInsertText(vRange, data, editor, attributes);
return;
}

case 'insertParagraph': {
handleInsertParagraph(vRange, editor);
return;
}

// Chrome and Safari on Mac: Backspace or Ctrl + H
case 'deleteContentBackward':
case 'deleteByCut': {
handleDeleteBackward(vRange, editor);
return;
}

// Chrome on Mac: Fn + Backspace or Ctrl + D
// Safari on Mac: Ctrl + K or Ctrl + D
case 'deleteContentForward': {
handleDeleteForward(editor, vRange);
return;
}

// On Mac: Option + Backspace
// On iOS: Hold the backspace for a while and the whole words will start to disappear
case 'deleteWordBackward': {
handleDeleteWordBackward(editor, vRange);
return;
}

// onMac: Fn + Option + Backspace
// onWindows: Control + Delete
case 'deleteWordForward': {
handleDeleteWordForward(editor, vRange);
return;
}

// deleteHardLineBackward: Safari on Mac: Cmd + Backspace
// deleteSoftLineBackward: Chrome on Mac: Cmd + Backspace
case 'deleteHardLineBackward':
case 'deleteSoftLineBackward': {
handleDeleteLine(editor, vRange);
return;
}
if (!editor.isVRangeValid(vRange)) return;

if (inputType === 'insertText') {
handleInsertText(vRange, data, editor, attributes);
} else if (
inputType === 'insertParagraph' ||
inputType === 'insertLineBreak'
) {
handleInsertParagraph(vRange, editor);
} else if (inputType.startsWith('delete')) {
handleDelete(vRange, editor);
} else {
return;
}
}
3 changes: 3 additions & 0 deletions tests/paragraph.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -360,6 +360,9 @@ test('remove all indent for a paragraph block', async ({ page }) => {
await pressTab(page);
await type(page, 'foo');
await assertBlockChildrenIds(page, '3', ['4']);
await assertRichTexts(page, ['hello', 'world', 'foo']);
await pressBackspaceWithShortKey(page);
await assertRichTexts(page, ['hello', 'world', '']);
await pressBackspaceWithShortKey(page);
await assertBlockChildrenIds(page, '1', ['2', '4']);
await assertBlockChildrenIds(page, '2', ['3']);
Expand Down

1 comment on commit 900fcf5

@github-actions
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Size Report

Bundles

Entry Size Gzip Brotli
examples/basic 10.6 MB (+75.5 kB) 2.09 MB (+16.1 kB) 1.31 MB (+10.3 kB)

Packages

Name Size Gzip Brotli
blocks 1.44 MB (+5.44 kB) 362 kB (+1.88 kB) 274 kB (+1.37 kB)
editor 8.84 kB 3.33 kB 2.91 kB
store 61.1 kB (+532 B) 17.7 kB (+156 B) 15.9 kB (+134 B)
virgo 29.6 kB (-1.48 kB) 8.47 kB (-280 B) 7.59 kB (-283 B)

Please sign in to comment.