editor: hacer que sea posible poner parentBlocks y reestructurar
This commit is contained in:
parent
c9d0fb87a4
commit
0602c6ccb8
6 changed files with 87 additions and 26 deletions
|
@ -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)
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue