import { marks } from 'editor/types/marks' import { blocks, li, EditorBlock } from 'editor/types/blocks' import { parentBlocks } from 'editor/types/parentBlocks' import { blockNames } from 'editor/utils' export interface EditorNode { selector: string, // la string es el nombre en la gran lista de types O 'text' // XXX: esto es un hack para no poner EditorNode dentro de EditorNode, // quizás podemos hacer que esto sea una función que retorna bool allowedChildren: string[], // * si es 'do-nothing', no hace nada si está vacío (esto es para cuando // permitís 'text' entonces se puede tipear adentro, ej: párrafo vacío) // * si es 'remove', sacamos el coso si está vacío. // ej: strong: { handleNothing: 'remove' } // * si es un block, insertamos el bloque y movemos la selección ahí // ej: ul: { handleNothing: li } handleEmpty: 'do-nothing' | 'remove' | EditorBlock, create: () => HTMLElement, } export const types: { [propName: string]: EditorNode } = { ...marks, ...blocks, li, ...parentBlocks, contentEl: { selector: '.editor-content', allowedChildren: [...blockNames, ...Object.keys(parentBlocks)], handleEmpty: blocks.paragraph, create: () => { throw new Error('se intentó crear contentEl') } }, br: { selector: 'br', allowedChildren: [], handleEmpty: 'do-nothing', create: () => { throw new Error('se intentó crear br') } }, } export function getType (node: Element): { typeName: string, type: EditorNode } | null { for (let [typeName, type] of Object.entries(types)) { if (node.matches(type.selector)) return { typeName, type } } return null } export function getValidChildren (node: Element, type: EditorNode): Node[] { return [...node.childNodes].filter(n => { // si permite texto y esto es un texto, es válido if (n.nodeType === Node.TEXT_NODE) return type.allowedChildren.includes('text') // si no es un elemento, no es válido if (!(n instanceof Element)) return false const t = getType(n) if (!t) return false return type.allowedChildren.includes(t.typeName) }) }