Import text-editor code into the repository

This commit is contained in:
Andrey Antukh 2024-11-19 17:05:30 +01:00
parent 68397edd4d
commit 04a0d867b0
65 changed files with 11112 additions and 7 deletions

View file

@ -0,0 +1,66 @@
/**
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*
* Copyright (c) KALEIDOS INC
*/
/**
* Command mutations
*/
export class CommandMutations {
#added = new Set();
#removed = new Set();
#updated = new Set();
constructor(added, updated, removed) {
if (added && Array.isArray(added)) this.#added = new Set(added);
if (updated && Array.isArray(updated)) this.#updated = new Set(updated);
if (removed && Array.isArray(removed)) this.#removed = new Set(removed);
}
get added() {
return this.#added;
}
get removed() {
return this.#removed;
}
get updated() {
return this.#updated;
}
clear() {
this.#added.clear();
this.#removed.clear();
this.#updated.clear();
}
dispose() {
this.#added.clear();
this.#added = null;
this.#removed.clear();
this.#removed = null;
this.#updated.clear();
this.#updated = null;
}
add(node) {
this.#added.add(node);
return this;
}
remove(node) {
this.#removed.add(node);
return this;
}
update(node) {
this.#updated.add(node);
return this;
}
}
export default CommandMutations;

View file

@ -0,0 +1,71 @@
import { describe, test, expect } from 'vitest';
import CommandMutations from './CommandMutations';
describe("CommandMutations", () => {
test("should create a new CommandMutations", () => {
const mutations = new CommandMutations();
expect(mutations).toHaveProperty("added");
expect(mutations).toHaveProperty("updated");
expect(mutations).toHaveProperty("removed");
});
test("should create an initialized new CommandMutations", () => {
const mutations = new CommandMutations([1], [2], [3]);
expect(mutations.added.size).toBe(1);
expect(mutations.updated.size).toBe(1);
expect(mutations.removed.size).toBe(1);
expect(mutations.added.has(1)).toBe(true);
expect(mutations.updated.has(2)).toBe(true);
expect(mutations.removed.has(3)).toBe(true);
});
test("should add an added node to a CommandMutations", () => {
const mutations = new CommandMutations();
mutations.add(1);
expect(mutations.added.has(1)).toBe(true);
});
test("should add an updated node to a CommandMutations", () => {
const mutations = new CommandMutations();
mutations.update(1);
expect(mutations.updated.has(1)).toBe(true);
});
test("should add an removed node to a CommandMutations", () => {
const mutations = new CommandMutations();
mutations.remove(1);
expect(mutations.removed.has(1)).toBe(true);
});
test("should clear a CommandMutations", () => {
const mutations = new CommandMutations();
mutations.add(1);
mutations.update(2);
mutations.remove(3);
expect(mutations.added.has(1)).toBe(true);
expect(mutations.added.size).toBe(1);
expect(mutations.updated.has(2)).toBe(true);
expect(mutations.updated.size).toBe(1);
expect(mutations.removed.has(3)).toBe(true);
expect(mutations.removed.size).toBe(1);
mutations.clear();
expect(mutations.added.size).toBe(0);
expect(mutations.added.has(1)).toBe(false);
expect(mutations.updated.size).toBe(0);
expect(mutations.updated.has(1)).toBe(false);
expect(mutations.removed.size).toBe(0);
expect(mutations.removed.has(1)).toBe(false);
});
test("should dispose a CommandMutations", () => {
const mutations = new CommandMutations();
mutations.add(1);
mutations.update(2);
mutations.remove(3);
mutations.dispose();
expect(mutations.added).toBe(null);
expect(mutations.updated).toBe(null);
expect(mutations.removed).toBe(null);
})
});

View file

@ -0,0 +1,22 @@
/**
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*
* Copyright (c) KALEIDOS INC
*/
/**
* Remove the current selection as part of a cut.
*
* @param {InputEvent} event
* @param {TextEditor} editor
* @param {SelectionController} selectionController
*/
export function deleteByCut(event, editor, selectionController) {
event.preventDefault();
if (selectionController.isCollapsed) {
throw new Error("This should be impossible");
}
return selectionController.removeSelected();
}

View file

@ -0,0 +1,53 @@
/**
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*
* Copyright (c) KALEIDOS INC
*/
/**
* delete the content directly before the caret position and this intention is
* not covered by another `inputType` or delete the selection with the
* selection collapsing to its start after the deletion.
*
* @param {InputEvent} event
* @param {TextEditor} editor
* @param {SelectionController} selectionController
*/
export function deleteContentBackward(event, editor, selectionController) {
event.preventDefault();
// If the editor is empty this is a no op.
if (editor.isEmpty) return;
// If not is collapsed AKA is a selection, then
// we removeSelected.
if (!selectionController.isCollapsed) {
return selectionController.removeSelected({ direction: 'backward' });
}
// If we're in a text node and the offset is
// greater than 0 (not at the start of the inline)
// we simple remove a character from the text.
if (selectionController.isTextFocus && selectionController.focusOffset > 0) {
return selectionController.removeBackwardText();
// If we're in a text node but we're at the end of the
// paragraph, we should merge the current paragraph
// with the following paragraph.
} else if (
selectionController.isTextFocus &&
selectionController.focusAtStart
) {
return selectionController.mergeBackwardParagraph();
// If we're at an inline or a line break paragraph
// and there's more than one paragraph, then we should
// remove the next paragraph.
} else if (
selectionController.isInlineFocus ||
selectionController.isLineBreakFocus
) {
return selectionController.removeBackwardParagraph();
}
}

View file

@ -0,0 +1,54 @@
/**
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*
* Copyright (c) KALEIDOS INC
*/
/**
* delete the content directly after the caret position and this intention is not covered by
* another inputType or delete the selection with the selection collapsing to its end after the deletion
*
* @param {InputEvent} event
* @param {TextEditor} editor
* @param {SelectionController} selectionController
*/
export function deleteContentForward(event, editor, selectionController) {
event.preventDefault();
// If the editor is empty this is a no op.
if (editor.isEmpty) return;
// If not is collapsed AKA is a selection, then
// we removeSelected.
if (!selectionController.isCollapsed) {
return selectionController.removeSelected({ direction: "forward" });
}
// If we're in a text node and the offset is
// greater than 0 (not at the start of the inline)
// we simple remove a character from the text.
if (selectionController.isTextFocus
&& selectionController.focusAtEnd) {
return selectionController.mergeForwardParagraph();
// If we're in a text node but we're at the end of the
// paragraph, we should merge the current paragraph
// with the following paragraph.
} else if (
selectionController.isTextFocus &&
selectionController.focusOffset >= 0
) {
return selectionController.removeForwardText();
// If we're at an inline or a line break paragraph
// and there's more than one paragraph, then we should
// remove the next paragraph.
} else if (
(selectionController.isInlineFocus ||
selectionController.isLineBreakFocus) &&
editor.numParagraphs > 1
) {
return selectionController.removeForwardParagraph();
}
}

View file

@ -0,0 +1,21 @@
/**
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*
* Copyright (c) KALEIDOS INC
*/
import { insertText } from "./insertText.js";
import { insertParagraph } from "./insertParagraph.js";
import { deleteByCut } from "./deleteByCut.js";
import { deleteContentBackward } from "./deleteContentBackward.js";
import { deleteContentForward } from "./deleteContentForward.js";
export default {
insertText,
insertParagraph,
deleteByCut,
deleteContentBackward,
deleteContentForward,
};

View file

@ -0,0 +1,23 @@
/**
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*
* Copyright (c) KALEIDOS INC
*/
/**
* Insert a paragraph
*
* @see https://w3c.github.io/input-events/#interface-InputEvent
* @param {InputEvent} event
* @param {TextEditor} editor
* @param {SelectionController} selectionController
*/
export function insertParagraph(event, editor, selectionController) {
event.preventDefault();
if (selectionController.isCollapsed) {
return selectionController.insertParagraph();
}
return selectionController.replaceWithParagraph();
}

View file

@ -0,0 +1,34 @@
/**
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*
* Copyright (c) KALEIDOS INC
*/
/**
* Insert typed plain text
*
* @see https://w3c.github.io/input-events/#interface-InputEvent
* @param {InputEvent} event
* @param {TextEditor} editor
* @param {SelectionController} selectionController
*/
export function insertText(event, editor, selectionController) {
event.preventDefault();
if (selectionController.isCollapsed) {
if (selectionController.isTextFocus) {
return selectionController.insertText(event.data);
} else if (selectionController.isLineBreakFocus) {
return selectionController.replaceLineBreak(event.data);
}
} else {
if (selectionController.isMultiParagraph) {
return selectionController.replaceParagraphs(event.data);
} else if (selectionController.isMultiInline) {
return selectionController.replaceInlines(event.data);
} else if (selectionController.isTextSame) {
return selectionController.replaceText(event.data);
}
}
}