102 lines
2.9 KiB
TypeScript
102 lines
2.9 KiB
TypeScript
|
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
|
||
|
})
|
||
|
}
|
||
|
}
|