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

102 lines
2.9 KiB
TypeScript
Raw Normal View History

import { Editor } from 'editor/editor'
import { safeGetSelection, safeGetRangeAt, moveChildren } 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
// EditorBlock o algo así)
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)
},
}
}
export const li: EditorBlock = makeBlock('li')
// XXX: si agregás algo acá, agregalo a blockNames
// (y probablemente le quieras hacer un botón en app/views/posts/attributes/_content.haml)
export const blocks: { [propName: string]: EditorBlock } = {
paragraph: makeBlock('p'),
h1: makeBlock('h1'),
h2: makeBlock('h2'),
h3: makeBlock('h3'),
h4: makeBlock('h4'),
h5: makeBlock('h5'),
h6: makeBlock('h6'),
unordered_list: {
...makeBlock('ul'),
allowedChildren: ['li'],
handleEmpty: li,
},
ordered_list: {
...makeBlock('ol'),
allowedChildren: ['li'],
handleEmpty: li,
},
}
export function setupButtons (editor: Editor): void {
for (const [ name, type ] of Object.entries(blocks)) {
const buttonEl = editor.toolbarEl.querySelector(`[data-editor-button="block-${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 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('Inesperado')
let replacementType = blockEl.matches(type.selector)
? blocks.paragraph
: type
const el = replacementType.create()
moveChildren(blockEl, el, null)
parentEl.replaceChild(el, blockEl)
sel.collapse(el)
return false
})
}
}