From 0602c6ccb824a8ed441108da2138cc2ddb4360c4 Mon Sep 17 00:00:00 2001 From: void Date: Fri, 12 Feb 2021 22:18:48 +0000 Subject: [PATCH] editor: hacer que sea posible poner parentBlocks y reestructurar --- app/javascript/editor/editor.ts | 7 +- app/javascript/editor/types.ts | 3 +- app/javascript/editor/types/blocks.ts | 20 ++---- app/javascript/editor/types/marks.ts | 8 ++- app/javascript/editor/types/parentBlocks.ts | 71 +++++++++++++++++++-- app/javascript/editor/utils.ts | 4 ++ 6 files changed, 87 insertions(+), 26 deletions(-) diff --git a/app/javascript/editor/editor.ts b/app/javascript/editor/editor.ts index a36634e..7d42e6e 100644 --- a/app/javascript/editor/editor.ts +++ b/app/javascript/editor/editor.ts @@ -1,9 +1,9 @@ import { storeContent, restoreContent } from 'editor/storage' import { isDirectChild, moveChildren, safeGetSelection, safeGetRangeAt } from 'editor/utils' -import { types, EditorNode, getValidChildren, getType } from 'editor/types' +import { types, getValidChildren, getType } from 'editor/types' import { setupButtons as setupMarksButtons } from 'editor/types/marks' -import { blocks, EditorBlock, setupButtons as setupBlocksButtons } from 'editor/types/blocks' -import { parentBlocks, parentBlockNames } from 'editor/types/parentBlocks' +import { setupButtons as setupBlocksButtons } from 'editor/types/blocks' +import { setupButtons as setupParentBlocksButtons } from 'editor/types/parentBlocks' export interface Editor { editorEl: HTMLElement, @@ -177,6 +177,7 @@ function setupEditor (editorEl: HTMLElement): void { // Setup botones setupMarksButtons(editor) setupBlocksButtons(editor) + setupParentBlocksButtons(editor) // Finally... routine(editor) diff --git a/app/javascript/editor/types.ts b/app/javascript/editor/types.ts index bea3744..91dfb4e 100644 --- a/app/javascript/editor/types.ts +++ b/app/javascript/editor/types.ts @@ -1,6 +1,7 @@ import { marks } from 'editor/types/marks' -import { blocks, li, blockNames, EditorBlock } from 'editor/types/blocks' +import { blocks, li, EditorBlock } from 'editor/types/blocks' import { parentBlocks } from 'editor/types/parentBlocks' +import { blockNames } from 'editor/utils' export interface EditorNode { selector: string, diff --git a/app/javascript/editor/types/blocks.ts b/app/javascript/editor/types/blocks.ts index e81ab01..55a7502 100644 --- a/app/javascript/editor/types/blocks.ts +++ b/app/javascript/editor/types/blocks.ts @@ -1,8 +1,10 @@ import { Editor } from 'editor/editor' -import { safeGetSelection, safeGetRangeAt, moveChildren } from 'editor/utils' +import { + safeGetSelection, safeGetRangeAt, + moveChildren, + markNames, parentBlockNames, +} from 'editor/utils' import { EditorNode, getType } from 'editor/types' -import { markNames } from 'editor/types/marks' -import { parentBlockNames } from 'editor/types/parentBlocks' // TODO: implementar multimedia como otro tipo! ya que no puede tener children // debe ser tratado distinto que los bloques (quizás EditorBlockWithText y @@ -11,22 +13,12 @@ import { parentBlockNames } from 'editor/types/parentBlocks' export interface EditorBlock extends EditorNode { } -export const blockNames = ['paragraph', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'unordered_list', 'ordered_list'] - function makeBlock (tag: string): EditorBlock { return { selector: tag, allowedChildren: [...markNames, 'text'], handleEmpty: 'do-nothing', - create: () => { - const el = document.createElement(tag) - return el - //moveChildren(el, newEl, null) - //el.parentNode!.replaceChild(newEl, el) - //// TODO: mantener la selección - //const sel = window.getSelection() - //sel && sel.collapse(newEl, 0) - }, + create: () => document.createElement(tag), } } diff --git a/app/javascript/editor/types/marks.ts b/app/javascript/editor/types/marks.ts index 04e9805..8fb97ab 100644 --- a/app/javascript/editor/types/marks.ts +++ b/app/javascript/editor/types/marks.ts @@ -1,8 +1,10 @@ import { Editor } from 'editor/editor' import { EditorNode } from 'editor/types' -import { safeGetSelection, safeGetRangeAt, moveChildren, splitNode } from 'editor/utils' - -export const markNames = ['bold', 'italic', 'deleted', 'underline', 'sub', 'super', 'mark', 'a'] +import { + safeGetSelection, safeGetRangeAt, + moveChildren, + markNames, +} from 'editor/utils' function makeMark (name: string, tag: string): EditorNode { return { diff --git a/app/javascript/editor/types/parentBlocks.ts b/app/javascript/editor/types/parentBlocks.ts index 71b611d..090d0ea 100644 --- a/app/javascript/editor/types/parentBlocks.ts +++ b/app/javascript/editor/types/parentBlocks.ts @@ -1,18 +1,24 @@ -import { blockNames, EditorBlock } from 'editor/types/blocks' +import { Editor } from 'editor/editor' +import { + safeGetSelection, safeGetRangeAt, + moveChildren, + blockNames, parentBlockNames, +} from 'editor/utils' +import { EditorNode, getType } from 'editor/types' -function makeParentBlock (tag: string, create: EditorBlock["create"]): EditorBlock { +function makeParentBlock (tag: string, create: EditorNode["create"]): EditorNode { return { selector: tag, allowedChildren: blockNames, handleEmpty: 'remove', - create: create, + create, } } // TODO: añadir blockquote // XXX: si agregás algo acá, probablemente le quieras hacer un botón // en app/views/posts/attributes/_content.haml -export const parentBlocks: { [propName: string]: EditorBlock } = { +export const parentBlocks: { [propName: string]: EditorNode } = { left: makeParentBlock('div[data-align=left]', () => { const el = document.createElement('div') el.dataset.align = 'left' @@ -30,4 +36,59 @@ export const parentBlocks: { [propName: string]: EditorBlock } = { }), } -export const parentBlockNames = Object.keys(parentBlocks) +export function setupButtons (editor: Editor): void { + for (const [ name, type ] of Object.entries(parentBlocks)) { + const buttonEl = editor.toolbarEl.querySelector( + `[data-editor-button="parentBlock-${name}"]` + ) + if (!buttonEl) continue + buttonEl.addEventListener("click", event => { + event.preventDefault() + + const sel = safeGetSelection(editor) + if (!sel) return + const range = safeGetRangeAt(sel) + if (!range) return + + // TODO: Esto solo mueve el bloque en el que está el final de la selección + // (anchorNode). quizás lo podemos hacer al revés (iterar desde contentEl + // para encontrar los bloques que están seleccionados y moverlos/cambiarles + // el parentBlock) + + let blockEl = sel.anchorNode + while (true) { + if (!blockEl) throw new Error('WTF') + if (!blockEl.parentElement) throw new Error('No pude encontrar contentEl!') + + let type = getType(blockEl.parentElement) + if (!type) throw new Error('La selección está en algo que no es un type!') + + if (type.typeName === 'contentEl' + || parentBlockNames.includes(type.typeName) + ) break + + blockEl = blockEl.parentElement + } + if (!(blockEl instanceof Element)) + throw new Error('La selección no está en un elemento!') + + const parentEl = blockEl.parentElement + if (!parentEl) + throw new Error('no') + + const replacementEl = type.create() + if (parentEl == editor.contentEl) { + // no está en un parentBlock + editor.contentEl.insertBefore(replacementEl, blockEl) + replacementEl.appendChild(blockEl) + } else { + // está en un parentBlock + moveChildren(parentEl, replacementEl, null) + editor.contentEl.replaceChild(replacementEl, parentEl) + } + sel.collapse(replacementEl) + + return false + }) + } +} diff --git a/app/javascript/editor/utils.ts b/app/javascript/editor/utils.ts index e76587b..0710bdd 100644 --- a/app/javascript/editor/utils.ts +++ b/app/javascript/editor/utils.ts @@ -1,5 +1,9 @@ import { Editor } from 'editor/editor' +export const blockNames = ['paragraph', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'unordered_list', 'ordered_list'] +export const markNames = ['bold', 'italic', 'deleted', 'underline', 'sub', 'super', 'mark', 'a'] +export const parentBlockNames = ['left', 'center', 'right'] + export function moveChildren (from: Element, to: Element, toRef: Node | null) { while (from.firstChild) to.insertBefore(from.firstChild, toRef) }