sutty/app/javascript/editor/types/marks.ts

103 lines
2.8 KiB
TypeScript

import { Editor } from "editor/editor";
import { EditorNode } from "editor/types";
import {
safeGetSelection,
safeGetRangeAt,
moveChildren,
markNames,
} from "editor/utils";
import { link } from "editor/types/link";
import { mark } from "editor/types/mark";
function makeMark(name: string, tag: string): EditorNode {
return {
selector: tag,
allowedChildren: [...markNames.filter((n) => n !== name), "text"],
handleEmpty: "remove",
create: () => document.createElement(tag),
};
}
// XXX: si agregás algo acá, agregalo a markNames
export const marks: { [propName: string]: EditorNode } = {
bold: makeMark("bold", "strong"),
italic: makeMark("italic", "em"),
deleted: makeMark("deleted", "del"),
underline: makeMark("underline", "u"),
sub: makeMark("sub", "sub"),
super: makeMark("super", "sup"),
mark,
link,
small: makeMark("small", "small"),
};
function recursiveFilterSelection(
node: Element,
selection: Selection,
selector: string
): Element[] {
let output: Element[] = [];
for (const child of [...node.children]) {
if (child.matches(selector) && selection.containsNode(child))
output.push(child);
output = [
...output,
...recursiveFilterSelection(child, selection, selector),
];
}
return output;
}
export function setupButtons(editor: Editor): void {
for (const [name, type] of Object.entries(marks)) {
const buttonEl = editor.toolbarEl.querySelector(
`[data-editor-button="mark-${name}"]`
);
if (!buttonEl) continue;
buttonEl.addEventListener("click", (event) => {
event.preventDefault();
const sel = safeGetSelection(editor);
if (!sel) return;
const range = safeGetRangeAt(sel);
if (!range) return;
let parentEl = range.commonAncestorContainer;
while (!(parentEl instanceof Element)) {
if (!parentEl.parentElement) return;
parentEl = parentEl.parentElement;
}
const existingMarks = recursiveFilterSelection(
parentEl,
sel,
type.selector
);
console.debug("marks encontradas:", existingMarks);
if (existingMarks.length > 0) {
const mark = existingMarks[0];
if (!mark.parentElement) throw new Error(":/");
moveChildren(mark, mark.parentElement, mark);
mark.parentElement.removeChild(mark);
} else {
if (range.commonAncestorContainer === editor.contentEl)
// TODO: mostrar error
return console.error(
"No puedo marcar cosas a través de distintos bloques!"
);
const tagEl = type.create(editor);
type.onClick && type.onClick(editor, tagEl);
tagEl.appendChild(range.extractContents());
range.insertNode(tagEl);
range.selectNode(tagEl);
}
return false;
});
}
}